Compare commits
103 Commits
v1.344.2
...
rf/extraDo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c90d0965a5 | ||
|
|
2c4c443f75 | ||
|
|
17c586ed8f | ||
|
|
afa73ad6ea | ||
|
|
e38f7e6e82 | ||
|
|
fa2f732182 | ||
|
|
75555f725c | ||
|
|
b9a73c5b82 | ||
|
|
f29c46a282 | ||
|
|
73175acc01 | ||
|
|
08476a3794 | ||
|
|
275c10b17a | ||
|
|
e9b310eee0 | ||
|
|
dd804839f0 | ||
|
|
9ac18c33bf | ||
|
|
dc68f3e939 | ||
|
|
97215a979f | ||
|
|
f8dc21542f | ||
|
|
cab248ceff | ||
|
|
6db033e36a | ||
|
|
a804e013c2 | ||
|
|
189a2815a0 | ||
|
|
4ef38233c1 | ||
|
|
ee7db8c8e3 | ||
|
|
183361df85 | ||
|
|
7c64f0a5b2 | ||
|
|
2a88fcbfd8 | ||
|
|
a859705efd | ||
|
|
e0479e05f7 | ||
|
|
b57afc8f68 | ||
|
|
0c661220cf | ||
|
|
53eeef0503 | ||
|
|
f626a361c4 | ||
|
|
ba76d8749a | ||
|
|
ff4ea86155 | ||
|
|
25a460be85 | ||
|
|
e5db2782f5 | ||
|
|
b2111b7d11 | ||
|
|
1042a06a12 | ||
|
|
0567d74abf | ||
|
|
3f0cd6349d | ||
|
|
f1f9fe513b | ||
|
|
a43923b326 | ||
|
|
9d8a75c3ca | ||
|
|
59d731c35d | ||
|
|
4bfda45c72 | ||
|
|
7007f14a10 | ||
|
|
da999d0f9b | ||
|
|
37c88272f5 | ||
|
|
97ee8c35ea | ||
|
|
a82184beb1 | ||
|
|
faaef3016d | ||
|
|
5c5b98e0b8 | ||
|
|
d166c563d9 | ||
|
|
4fa9025786 | ||
|
|
a1dc0fd7c2 | ||
|
|
a0813939fe | ||
|
|
7c460888be | ||
|
|
f5e8d71f0b | ||
|
|
a2b88c0080 | ||
|
|
2e7c5b0ea9 | ||
|
|
c9c017e27a | ||
|
|
866fcb1a79 | ||
|
|
6d4b8a2e4f | ||
|
|
9c152d96c9 | ||
|
|
f3e8a81923 | ||
|
|
ecb0e9b961 | ||
|
|
a7250bfae7 | ||
|
|
4cd733b67d | ||
|
|
b753e4d06c | ||
|
|
b904a36356 | ||
|
|
cfab1853ab | ||
|
|
b56f77b1da | ||
|
|
eaf5b8ebe5 | ||
|
|
ddec620324 | ||
|
|
a64b099084 | ||
|
|
631b981b8e | ||
|
|
076d2d6fe7 | ||
|
|
95cdeacef9 | ||
|
|
25024a920d | ||
|
|
05d2197618 | ||
|
|
2df9c69023 | ||
|
|
24e3a25ca4 | ||
|
|
c94c76f59b | ||
|
|
cf4d0346f6 | ||
|
|
96dfacbfc8 | ||
|
|
952adff138 | ||
|
|
1cc8891fb2 | ||
|
|
d518e2359d | ||
|
|
e0ac22ba4a | ||
|
|
21b9accca1 | ||
|
|
dc6144219f | ||
|
|
28afe59a4a | ||
|
|
4f0e111563 | ||
|
|
c19f330965 | ||
|
|
96767ec042 | ||
|
|
d0f47f4b03 | ||
|
|
6c2420f118 | ||
|
|
320ba754a4 | ||
|
|
949b9b4787 | ||
|
|
2344077b34 | ||
|
|
4b7ff22cc0 | ||
|
|
c96697928f |
55
.github/workflows/build_cli_image.yml
vendored
Normal file
55
.github/workflows/build_cli_image.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}-cli
|
||||
|
||||
name: Publish cli image
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish_cli:
|
||||
runs-on: ubicloud
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: depot/setup-action@v1
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push publicly
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
file: "./docker/DockerfileCli"
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
${{ steps.meta.outputs.tags }}
|
||||
labels: |
|
||||
${{ steps.meta.outputs.labels }}
|
||||
org.opencontainers.image.licenses=AGPLv3
|
||||
5
.github/workflows/change-versions.yml
vendored
5
.github/workflows/change-versions.yml
vendored
@@ -17,5 +17,8 @@ jobs:
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: update lockfile
|
||||
run: cd backend && cargo generate-lockfile
|
||||
run: |
|
||||
cd backend
|
||||
cargo generate-lockfile
|
||||
cargo update -p bit-vec@0.7.0 --precise 0.6.3
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
|
||||
50
.github/workflows/docker-310.yml.archived
vendored
50
.github/workflows/docker-310.yml.archived
vendored
@@ -1,50 +0,0 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
name: Build windmill:python310
|
||||
on: workflow_dispatch
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-python3.10
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build_ee:
|
||||
runs-on: ubicloud
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: depot/setup-action@v1
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Edit python version
|
||||
run: |
|
||||
sed -i 's/python:3.11.4/python:3.10.12/g' Dockerfile
|
||||
|
||||
- name: Build and push publicly ee
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:python310
|
||||
labels: |
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
46
.github/workflows/docker-mssql.yml.archived
vendored
46
.github/workflows/docker-mssql.yml.archived
vendored
@@ -1,46 +0,0 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
name: Build windmill:mssql
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-mssql
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build_ee:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: depot/setup-action@v1
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push publicly ee
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: ./docker/DockerfileMssql
|
||||
build-args: |
|
||||
features=enterprise
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:mssql
|
||||
labels: |
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
215
CHANGELOG.md
215
CHANGELOG.md
@@ -1,5 +1,220 @@
|
||||
# Changelog
|
||||
|
||||
## [1.355.0](https://github.com/windmill-labs/windmill/compare/v1.354.0...v1.355.0) (2024-06-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add wmill cli image ([fa2f732](https://github.com/windmill-labs/windmill/commit/fa2f732182c8ac103c3fdcb0cfe5eecba2474382))
|
||||
* **frontend:** Add context for inner components of list, groups and … ([#3974](https://github.com/windmill-labs/windmill/issues/3974)) ([73175ac](https://github.com/windmill-labs/windmill/commit/73175acc018651eb0d3c161c6176e96576178813))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** add tooltip to schedule pausing ([#3984](https://github.com/windmill-labs/windmill/issues/3984)) ([08476a3](https://github.com/windmill-labs/windmill/commit/08476a379456039cc8569125ca3535008d6f7bea))
|
||||
* hide draft only items in pickers and from ops ([#3977](https://github.com/windmill-labs/windmill/issues/3977)) ([dd80483](https://github.com/windmill-labs/windmill/commit/dd804839f07aa56f6707fa122fa8e8bbcef81f6e))
|
||||
* parallel branchall disordered skip failure retrieval ([#3975](https://github.com/windmill-labs/windmill/issues/3975)) ([e9b310e](https://github.com/windmill-labs/windmill/commit/e9b310eee0d73f3518c85b572e05473c449959a0))
|
||||
* remove useless metrics ([#3962](https://github.com/windmill-labs/windmill/issues/3962)) ([f29c46a](https://github.com/windmill-labs/windmill/commit/f29c46a282e7cb1a9f093a3089e4eefd24a7bc1e))
|
||||
|
||||
## [1.354.0](https://github.com/windmill-labs/windmill/compare/v1.353.0...v1.354.0) (2024-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** app editor help box ([#3970](https://github.com/windmill-labs/windmill/issues/3970)) ([cab248c](https://github.com/windmill-labs/windmill/commit/cab248cefff73bab818c477b4e40220b3d81eac9))
|
||||
* schedule pausing ([#3976](https://github.com/windmill-labs/windmill/issues/3976)) ([f8dc215](https://github.com/windmill-labs/windmill/commit/f8dc21542fd3a57e4272d874a034a3af1cdd8348))
|
||||
|
||||
## [1.353.0](https://github.com/windmill-labs/windmill/compare/v1.352.0...v1.353.0) (2024-06-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** add an indicator when a for loop has no iterator expression ([#3961](https://github.com/windmill-labs/windmill/issues/3961)) ([ee7db8c](https://github.com/windmill-labs/windmill/commit/ee7db8c8e33eb990279d5badc95dc54b0c6124dd))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** Add missing on change event when connecting an input ([#3964](https://github.com/windmill-labs/windmill/issues/3964)) ([a804e01](https://github.com/windmill-labs/windmill/commit/a804e013c2672478469edd5e512d7ce63ba92bcd))
|
||||
* **frontend:** improve disable style of the select component ([#3938](https://github.com/windmill-labs/windmill/issues/3938)) ([183361d](https://github.com/windmill-labs/windmill/commit/183361df85e5efbcea4a53bddc3e641cbf73acd2))
|
||||
* Improve cancel selected jobs action ([#3960](https://github.com/windmill-labs/windmill/issues/3960)) ([4ef3823](https://github.com/windmill-labs/windmill/commit/4ef38233c1a21b143456fe79d5675b2ad8f3606e))
|
||||
|
||||
## [1.352.0](https://github.com/windmill-labs/windmill/compare/v1.351.0...v1.352.0) (2024-06-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add extra metadata to folders/resources/types + path prefix filtering for all + improve groups UI ([#3936](https://github.com/windmill-labs/windmill/issues/3936)) ([b57afc8](https://github.com/windmill-labs/windmill/commit/b57afc8f68f691ad5ef7fb16aafe6fae7d1138e9))
|
||||
* fallback to default hub if official script not found on private hub ([#3951](https://github.com/windmill-labs/windmill/issues/3951)) ([0c66122](https://github.com/windmill-labs/windmill/commit/0c661220cf1fb7eb37ad7f39b1fa6d29570edda2))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cache wasm ([53eeef0](https://github.com/windmill-labs/windmill/commit/53eeef05032f04637249c2557d6d864f9446a9a1))
|
||||
* improve perf of forloop with parallelism massively ([e0479e0](https://github.com/windmill-labs/windmill/commit/e0479e05f7b1a212906afd8c5e268f3d48266311))
|
||||
|
||||
## [1.351.0](https://github.com/windmill-labs/windmill/compare/v1.350.3...v1.351.0) (2024-06-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** improve approval form edition + add a delete button t… ([#3946](https://github.com/windmill-labs/windmill/issues/3946)) ([25a460b](https://github.com/windmill-labs/windmill/commit/25a460be858ce020c40cd120e5aef2a79ba362d6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve support of relative paths in subflows and apps ([ba76d87](https://github.com/windmill-labs/windmill/commit/ba76d8749a9524ed356611c09f874da60f3b954a))
|
||||
|
||||
## [1.350.3](https://github.com/windmill-labs/windmill/compare/v1.350.2...v1.350.3) (2024-06-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** fix push flow support for windows ([1042a06](https://github.com/windmill-labs/windmill/commit/1042a06a12ae31d31facd1dfab10cbd2b10c86f5))
|
||||
|
||||
## [1.350.2](https://github.com/windmill-labs/windmill/compare/v1.350.1...v1.350.2) (2024-06-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** support windows more thoroughly + fix generate flow locks ([#3944](https://github.com/windmill-labs/windmill/issues/3944)) ([3f0cd63](https://github.com/windmill-labs/windmill/commit/3f0cd6349de37358f3942772254e2509383305b4))
|
||||
|
||||
## [1.350.1](https://github.com/windmill-labs/windmill/compare/v1.350.0...v1.350.1) (2024-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** fix inital sync pull generating wrong flow lockfile ([a43923b](https://github.com/windmill-labs/windmill/commit/a43923b3266b7b5781b0a5dc672c401377ae6e20))
|
||||
|
||||
## [1.350.0](https://github.com/windmill-labs/windmill/compare/v1.349.1...v1.350.0) (2024-06-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** add indicator when required field are missing ([#3935](https://github.com/windmill-labs/windmill/issues/3935)) ([7007f14](https://github.com/windmill-labs/windmill/commit/7007f14a1073f8f43b2500fae9c75d4222928802))
|
||||
|
||||
## [1.349.1](https://github.com/windmill-labs/windmill/compare/v1.349.0...v1.349.1) (2024-06-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* show workers without worker groups ([5c5b98e](https://github.com/windmill-labs/windmill/commit/5c5b98e0b8f0926d5f81c1894de8d367201da2d8))
|
||||
|
||||
## [1.349.0](https://github.com/windmill-labs/windmill/compare/v1.348.2...v1.349.0) (2024-06-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** improve range ([#3924](https://github.com/windmill-labs/windmill/issues/3924)) ([a1dc0fd](https://github.com/windmill-labs/windmill/commit/a1dc0fd7c22244d97429dbb65ded23e27badec6a))
|
||||
|
||||
## [1.348.2](https://github.com/windmill-labs/windmill/compare/v1.348.1...v1.348.2) (2024-06-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add jobid to background runnables and fix zindex issue of inline script editor ([a2b88c0](https://github.com/windmill-labs/windmill/commit/a2b88c0080b8cfa747bf221bde9d22781e8034a9))
|
||||
* **frontend:** fix full screen mode of the EvalInputEditor ([#3928](https://github.com/windmill-labs/windmill/issues/3928)) ([c9c017e](https://github.com/windmill-labs/windmill/commit/c9c017e27a71bfe618974fb0906283a6c1a82aa4))
|
||||
* handle past inputs with oneOf ([#3932](https://github.com/windmill-labs/windmill/issues/3932)) ([f5e8d71](https://github.com/windmill-labs/windmill/commit/f5e8d71f0b9e527d3658547f3cb193b6dbe4bcb0))
|
||||
|
||||
## [1.348.1](https://github.com/windmill-labs/windmill/compare/v1.348.0...v1.348.1) (2024-06-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle better single step parallel flows ([6d4b8a2](https://github.com/windmill-labs/windmill/commit/6d4b8a2e4f1ef5b067c2e04600ac5defa489141c))
|
||||
|
||||
## [1.348.0](https://github.com/windmill-labs/windmill/compare/v1.347.1...v1.348.0) (2024-06-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for bytea in pg ([#3926](https://github.com/windmill-labs/windmill/issues/3926)) ([b753e4d](https://github.com/windmill-labs/windmill/commit/b753e4d06c431035084ba325be7573354cd3b621))
|
||||
* **frontend:** Enable changing kind for string properties ([#3925](https://github.com/windmill-labs/windmill/issues/3925)) ([cfab185](https://github.com/windmill-labs/windmill/commit/cfab1853abb3557a0efc14e4d5ca05872ba27f76))
|
||||
* **frontend:** worker page improvements ([#3921](https://github.com/windmill-labs/windmill/issues/3921)) ([b904a36](https://github.com/windmill-labs/windmill/commit/b904a36356849f854aaf6e91b9767653d7c6802b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** fix InlineScriptEditor zIndex ([#3922](https://github.com/windmill-labs/windmill/issues/3922)) ([b56f77b](https://github.com/windmill-labs/windmill/commit/b56f77b1da10085b9f4436a2aac5ba13cf14bdaf))
|
||||
|
||||
## [1.347.1](https://github.com/windmill-labs/windmill/compare/v1.347.0...v1.347.1) (2024-06-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add more debug logs around http client errors ([a64b099](https://github.com/windmill-labs/windmill/commit/a64b0990846c41988b4d1717123672e2a59289bd))
|
||||
* add more debug logs around http client errors ([631b981](https://github.com/windmill-labs/windmill/commit/631b981b8e75165d4dc2ef706b415af4a1384a48))
|
||||
|
||||
## [1.347.0](https://github.com/windmill-labs/windmill/compare/v1.346.2...v1.347.0) (2024-06-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** UI customisation improvements + add support for object enums ([#3910](https://github.com/windmill-labs/windmill/issues/3910)) ([25024a9](https://github.com/windmill-labs/windmill/commit/25024a920db2956055a4eec6691f761c3048f6ee))
|
||||
* oneOf inputs ([#3893](https://github.com/windmill-labs/windmill/issues/3893)) ([2df9c69](https://github.com/windmill-labs/windmill/commit/2df9c6902369c0f9e9dde98ddcf9d06d38646a3e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix permission for scoped tokens on jobs_u paths ([05d2197](https://github.com/windmill-labs/windmill/commit/05d219761892508db8ef9a46ffaf23bac26207f3))
|
||||
|
||||
## [1.346.2](https://github.com/windmill-labs/windmill/compare/v1.346.1...v1.346.2) (2024-06-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** revert range ([#3916](https://github.com/windmill-labs/windmill/issues/3916)) ([c94c76f](https://github.com/windmill-labs/windmill/commit/c94c76f59bfbd852cfc486edf4a51ee6ab8d4501))
|
||||
|
||||
## [1.346.1](https://github.com/windmill-labs/windmill/compare/v1.346.0...v1.346.1) (2024-06-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* job perms and job logs grants ([#3914](https://github.com/windmill-labs/windmill/issues/3914)) ([96dfacb](https://github.com/windmill-labs/windmill/commit/96dfacbfc8a2e418a39a7dc2ebc1082f00ef510f))
|
||||
|
||||
## [1.346.0](https://github.com/windmill-labs/windmill/compare/v1.345.2...v1.346.0) (2024-06-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* accelerate bun through caches ([#3909](https://github.com/windmill-labs/windmill/issues/3909)) ([28afe59](https://github.com/windmill-labs/windmill/commit/28afe59a4ab4e97a18e4ca7a893d83c329ee4078))
|
||||
* **frontend:** event handlers ([#3902](https://github.com/windmill-labs/windmill/issues/3902)) ([4f0e111](https://github.com/windmill-labs/windmill/commit/4f0e111563f5f5ab7b280aa98a89f48cb3637e59))
|
||||
* replace ephemeral tokens by jwt ([#3908](https://github.com/windmill-labs/windmill/issues/3908)) ([21b9acc](https://github.com/windmill-labs/windmill/commit/21b9accca1a8ab5c728fce15b3a7890375f11274))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix workflow as code ([d518e23](https://github.com/windmill-labs/windmill/commit/d518e2359d522df3c8cf23baaf444eabfb11246f))
|
||||
* job perms for agent mode ([#3911](https://github.com/windmill-labs/windmill/issues/3911)) ([e0ac22b](https://github.com/windmill-labs/windmill/commit/e0ac22ba4a59da4ca6c548de1ce0b5f394a49c66))
|
||||
|
||||
## [1.345.2](https://github.com/windmill-labs/windmill/compare/v1.345.1...v1.345.2) (2024-06-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add database grants to oustanding_wait_time table ([96767ec](https://github.com/windmill-labs/windmill/commit/96767ec042b12af6c1dfb5fa1263847934975cb9))
|
||||
|
||||
## [1.345.1](https://github.com/windmill-labs/windmill/compare/v1.345.0...v1.345.1) (2024-06-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correct exception handling in PHP wrapper ([#3901](https://github.com/windmill-labs/windmill/issues/3901)) ([320ba75](https://github.com/windmill-labs/windmill/commit/320ba754a4dcfb096807cfedbab8264be11dfb32))
|
||||
* enums derivable from code ([6c2420f](https://github.com/windmill-labs/windmill/commit/6c2420f118d5665eca9ccc7a4f0b2ccd018738aa))
|
||||
|
||||
## [1.345.0](https://github.com/windmill-labs/windmill/compare/v1.344.3...v1.345.0) (2024-06-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** Generated UI editor + Schema Form complete refactor ([#3835](https://github.com/windmill-labs/windmill/issues/3835)) ([2344077](https://github.com/windmill-labs/windmill/commit/2344077b340ba22e40eb5ddb85c70f21c0c9e801))
|
||||
|
||||
## [1.344.3](https://github.com/windmill-labs/windmill/compare/v1.344.2...v1.344.3) (2024-06-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix dependency tracking for single scripts ([c966979](https://github.com/windmill-labs/windmill/commit/c96697928fa0ef3cd4507f377a73f3b51560fbf7))
|
||||
|
||||
## [1.344.2](https://github.com/windmill-labs/windmill/compare/v1.344.1...v1.344.2) (2024-06-11)
|
||||
|
||||
|
||||
|
||||
28
Dockerfile
28
Dockerfile
@@ -23,6 +23,8 @@ ENV SQLX_OFFLINE=true
|
||||
|
||||
FROM node:20-alpine as frontend
|
||||
|
||||
ARG WITH_DENO=true
|
||||
|
||||
# install dependencies
|
||||
WORKDIR /frontend
|
||||
COPY ./frontend/package.json ./frontend/package-lock.json ./
|
||||
@@ -37,6 +39,7 @@ COPY /backend/windmill-api/build_openapi.sh /backend/windmill-api/build_openapi.
|
||||
|
||||
RUN cd /backend/windmill-api && . ./build_openapi.sh
|
||||
COPY /backend/parsers/windmill-parser-wasm/pkg/ /backend/parsers/windmill-parser-wasm/pkg/
|
||||
COPY /typescript-client/docs/ /frontend/static/tsdocs/
|
||||
|
||||
RUN npm run generate-backend-client
|
||||
ENV NODE_OPTIONS "--max-old-space-size=8192"
|
||||
@@ -84,12 +87,17 @@ 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.44.1/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.44.1/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
|
||||
|
||||
RUN unzip deno.zip && rm deno.zip
|
||||
|
||||
ARG BUILD_ENV=copy
|
||||
|
||||
# FROM alpine as build_copy
|
||||
# ONBUILD COPY file /file
|
||||
|
||||
# FROM alpine as build_no_copy
|
||||
# ONBUILD RUN echo "I don't copy"
|
||||
|
||||
FROM ${PYTHON_IMAGE}
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
@@ -101,6 +109,10 @@ ARG APP=/usr/src/app
|
||||
ARG WITH_POWERSHELL=true
|
||||
ARG WITH_KUBECTL=true
|
||||
ARG WITH_HELM=true
|
||||
ARG WITH_GO=true
|
||||
ARG WITH_BUN=true
|
||||
ARG WITH_PYTHON=true
|
||||
|
||||
|
||||
|
||||
RUN apt-get update \
|
||||
@@ -136,7 +148,8 @@ RUN if [ "$WITH_KUBECTL" = "true" ]; then \
|
||||
else echo 'Building the image without kubectl'; fi
|
||||
|
||||
|
||||
RUN set -eux; \
|
||||
RUN if [ "$WITH_GO" = "true" ]; then \
|
||||
set -eux; \
|
||||
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
|
||||
case "$arch" in \
|
||||
'amd64') \
|
||||
@@ -150,7 +163,8 @@ RUN set -eux; \
|
||||
;; \
|
||||
*) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \
|
||||
esac; \
|
||||
wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz";
|
||||
wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz"; \
|
||||
else echo 'Building the image without go'; fi
|
||||
|
||||
ENV PATH="${PATH}:/usr/local/go/bin"
|
||||
ENV GO_PATH=/usr/local/go/bin/go
|
||||
@@ -159,7 +173,9 @@ RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
|
||||
RUN apt-get -y update && apt-get install -y curl nodejs awscli
|
||||
|
||||
# go build is slower the first time it is ran, so we prewarm it in the build
|
||||
RUN mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm && printf "package foo\nimport (\"fmt\")\nfunc main() { fmt.Println(42) }" > warm.go && go mod tidy && go build -x && rm -rf /tmp/gobuildwarm
|
||||
RUN if [ "$WITH_GO" = "true" ]; then \
|
||||
mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm && printf "package foo\nimport (\"fmt\")\nfunc main() { fmt.Println(42) }" > warm.go && go mod tidy && go build -x && rm -rf /tmp/gobuildwarm \
|
||||
else echo 'Building the image without go'; fi
|
||||
|
||||
ENV TZ=Etc/UTC
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT created_at FROM metrics WHERE id = 'author_count' ORDER BY created_at DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "021be0f26ea87e587e656b24a9a94538efbf54a1447a3898e19773789cfc9063"
|
||||
}
|
||||
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -34,6 +44,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue SET last_ping = now() WHERE id = $1 AND workspace_id = $2",
|
||||
"query": "UPDATE queue SET last_ping = now() WHERE id = $1 AND workspace_id = $2 AND canceled = false",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -11,5 +11,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "45c9ecf8b1f8cbca7c75dab24a1eb6da8ceb45258ee5817ec71e73bebbe415bd"
|
||||
"hash": "099e7c7a66968575f896e0c11ecd9cfe9a2ec315d6589e940be157a0563f81af"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue SET suspend = suspend - 1 WHERE id = $1 AND suspend > 0",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0c349ac832cbf055fce755c8e2081eacf2b836b0396ec48a6cc2405fb7aa76de"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO metrics (id, value) VALUES ('worker_usage', $1)",
|
||||
"query": "INSERT INTO metrics (id, value) VALUES ('telemetry', $1)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -10,5 +10,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5ce9a9e0669b299b998bc9080d24f8d6f56eb049bec2e0745d27ca3a4fc0c296"
|
||||
"hash": "0cb84cbb9083d967cc8be1cccab5be61080c1003eef51eea41862b25c2b93de6"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue SET running = false, started_at = null WHERE id = $1",
|
||||
"query": "UPDATE queue SET running = false, started_at = null WHERE id = $1 AND canceled = false",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -10,5 +10,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c05be905e46c5b0a2186ba859a725495e55df7f2ad839aea22d0286525eb823e"
|
||||
"hash": "215e0d320a304c8cb9ef12e7ea98a4eafb2456c123f9b6b96bb4ba2409166e5a"
|
||||
}
|
||||
14
backend/.sqlx/query-262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf.json
generated
Normal file
14
backend/.sqlx/query-262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue\n SET last_ping = now()\n WHERE id = $1 AND last_ping < now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM metrics \n WHERE (id = 'author_count' OR id = 'operator_count' OR id = 'worker_usage') AND created_at < NOW() - INTERVAL '6 month'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3667d72d23e8c35ab96d5e5d1fbfd94a8a9f74b6398e21f13c206c3f9427c6a9"
|
||||
}
|
||||
@@ -35,31 +35,41 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "is_expired",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 9,
|
||||
"name": "is_refreshed",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 10,
|
||||
"name": "refresh_error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 11,
|
||||
"name": "is_linked",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 12,
|
||||
"name": "is_oauth?",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 13,
|
||||
"name": "account",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
@@ -77,6 +87,8 @@
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO token\n (workspace_id, token, owner, label, expiration, super_admin, email, job)\n VALUES ($1, $2, $3, $4, now() + ($5 || ' seconds')::interval, $6, $7, $8)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "42cb4bc3abc8cba2193a7fbbae8275c1bc830081a43e3a4a897c41b6b3099b1d"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,10 +9,11 @@
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "489a62b5943a7a21ce487aa7b72a63dfc6300dd93bc29f5ec4cb1bfc471ad0bf"
|
||||
"hash": "42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4"
|
||||
}
|
||||
@@ -130,6 +130,11 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "paused_until",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"name": "jobs",
|
||||
"type_info": "JsonArray"
|
||||
}
|
||||
@@ -167,6 +172,7 @@
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
null
|
||||
]
|
||||
},
|
||||
|
||||
@@ -32,6 +32,16 @@
|
||||
"ordinal": 5,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -45,7 +55,9 @@
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "45e4d13f5806122faecdb1d9ab18159555b652869a036b006f4a151e999b17b7"
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "584cb984ea6528baac48c5c437ad2ee3bef92e3fada73dcf519147964c0f4f4a"
|
||||
}
|
||||
16
backend/.sqlx/query-5eb1e072a6cc6ace25a5806b5adfdc0ae5c89c556daf491f39e629951a912c6c.json
generated
Normal file
16
backend/.sqlx/query-5eb1e072a6cc6ace25a5806b5adfdc0ae5c89c556daf491f39e629951a912c6c.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5eb1e072a6cc6ace25a5806b5adfdc0ae5c89c556daf491f39e629951a912c6c"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, $2, $3, $4, $5)",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, now())",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,10 +9,12 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Jsonb"
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e33d0bec7e08c0bb4e6f26c93a636707714699e8984b9f537085e773849c8b43"
|
||||
"hash": "5f3f1f1ca72b0392f227d22adef9ef795049e60a823e8ce754a3f9d91ef1b6bb"
|
||||
}
|
||||
14
backend/.sqlx/query-64a3b92ce4366518423bc536efe33415b43b325d7380e0c7062c392c7ba51e59.json
generated
Normal file
14
backend/.sqlx/query-64a3b92ce4366518423bc536efe33415b43b325d7380e0c7062c392c7ba51e59.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM job_perms WHERE job_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "64a3b92ce4366518423bc536efe33415b43b325d7380e0c7062c392c7ba51e59"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6e6d781fe63602541f7cb4806d5ddde0792e1861ea8761b4875677c3d0594a86"
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, description = $4, resource_type = $5",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "726f77c2c1ef63eece7a1176bcbb916987fc29b04b889544a064dc7cd5eab36f"
|
||||
}
|
||||
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -33,6 +43,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
15
backend/.sqlx/query-7ded28aae42230da6e3aa6fa952268e8e15cc8b16770c20ec6be2a9e82ab93c9.json
generated
Normal file
15
backend/.sqlx/query-7ded28aae42230da6e3aa6fa952268e8e15cc8b16770c20ec6be2a9e82ab93c9.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7ded28aae42230da6e3aa6fa952268e8e15cc8b16770c20ec6be2a9e82ab93c9"
|
||||
}
|
||||
44
backend/.sqlx/query-8480792eefdd0f31a49afec0f91262a6973c068da5413b154eff9b9da90db95f.json
generated
Normal file
44
backend/.sqlx/query-8480792eefdd0f31a49afec0f91262a6973c068da5413b154eff9b9da90db95f.json
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT worker, worker_instance, vcpus, ping_at, started_at FROM worker_ping WHERE ping_at > now() - interval '30 days'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "worker",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "worker_instance",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "vcpus",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "ping_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "started_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "8480792eefdd0f31a49afec0f91262a6973c068da5413b154eff9b9da90db95f"
|
||||
}
|
||||
21
backend/.sqlx/query-8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047.json
generated
Normal file
21
backend/.sqlx/query-8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047.json
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n values ($1, $2, $3, $4, $5, $6, $7, $8) \n ON CONFLICT (job_id) DO UPDATE SET email = $2, username = $3, is_admin = $4, is_operator = $5, folders = $6, groups = $7, workspace_id = $8",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"JsonbArray",
|
||||
"TextArray",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM dependency_map\n WHERE importer_path = $1 AND importer_kind = $3::text::IMPORTER_KIND\n AND workspace_id = $2 AND importer_node_id = $4",
|
||||
"query": "DELETE FROM dependency_map\n WHERE importer_path = $1 AND importer_kind = $3::text::IMPORTER_KIND\n AND workspace_id = $2 AND ($4::text IS NULL OR importer_node_id = $4::text)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -13,5 +13,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "20db481797e151cb20e986d5aeb82fc2426a7abd277313dff326a667b956fdee"
|
||||
"hash": "91da4514dfee9a2a5c7c633bc28f8445eef286bd69368bf302d3279494610780"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "98c8e2c58b38beedcb474251c2554a2f1b768ec7ed1a419313f4842f26425e5d"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -27,6 +27,21 @@
|
||||
"ordinal": 4,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "summary",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -41,8 +56,11 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "8543f029d9784234e4c6a6dcd7b03e62d544b98be261334ee210594e0bb839f2"
|
||||
"hash": "9c6d44ffae63b4050ef3a66cb05d5a4ee50dea4b30093df2e0ceb684201ca4b0"
|
||||
}
|
||||
15
backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json
generated
Normal file
15
backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue SET canceled = true, canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = $3 AND workspace_id = $4 RETURNING id",
|
||||
"query": "UPDATE queue SET canceled = true, canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = $3 AND workspace_id = $4 AND canceled = false RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -21,5 +21,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f1f7a27738deae46de6acc10cbff701ff0c3872cc5b387e304bd9a058bb0c8a8"
|
||||
"hash": "aa923ddc251f5daf4bf075cb251dfddf87877f12fbbc8fbf3cfd08f43bbad8a0"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder\n (workspace_id, name, display_name, owners, extra_perms)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING",
|
||||
"query": "INSERT INTO folder\n (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,10 +9,11 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Jsonb"
|
||||
"Jsonb",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f2aee3ae39c90e40dd1835befc339e5381cc9104933cf8e90d840a9bf638ff52"
|
||||
"hash": "ad3b0176e3d41a0c1d79a8eb59da2a9cc4fee3473b3f6ea69f397d91e9356ba1"
|
||||
}
|
||||
@@ -18,8 +18,8 @@
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
|
||||
|
||||
15
backend/.sqlx/query-b7ac3383a058ab3bbd7280300cebc5f81eeb9e96622ad2557f810a8b91641b89.json
generated
Normal file
15
backend/.sqlx/query-b7ac3383a058ab3bbd7280300cebc5f81eeb9e96622ad2557f810a8b91641b89.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b7ac3383a058ab3bbd7280300cebc5f81eeb9e96622ad2557f810a8b91641b89"
|
||||
}
|
||||
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -33,6 +43,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id FROM queue WHERE parent_job = $1 AND suspend > 0 ORDER by suspend",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b8d97d300ffe6fae99f2396ae07cef03903752d17ce3fdadca47d86da75139aa"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource SET path = $1, value = $2 WHERE path = $3 AND workspace_id = $4",
|
||||
"query": "UPDATE resource SET path = $1, value = $2, edited_at = now() WHERE path = $3 AND workspace_id = $4",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -13,5 +13,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8c0131a9cc61f2daa258d49767242bcaab6bb34a977ff7fb0c18aa9202d11f47"
|
||||
"hash": "be10033b37dc9cbc38cc56afa8ef93cd7dbe267525d78cbfea87bb7c7cbf8ec4"
|
||||
}
|
||||
16
backend/.sqlx/query-c2a6e8f38d2293c45ede26b89a976f1a32f5bc2323a28f640cf3301a299f1cf4.json
generated
Normal file
16
backend/.sqlx/query-c2a6e8f38d2293c45ede26b89a976f1a32f5bc2323a28f640cf3301a299f1cf4.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE folder SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c2a6e8f38d2293c45ede26b89a976f1a32f5bc2323a28f640cf3301a299f1cf4"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, resource_type)\n VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -8,10 +8,11 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ad9ccde8d831461f1f312d530867714577a825cd40d6655ed61b0b343b5d4482"
|
||||
"hash": "c4ee16065fa021cf6443c2f29f1ca986b9730eae9df26dd33c3ea21e35df62c4"
|
||||
}
|
||||
19
backend/.sqlx/query-c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e.json
generated
Normal file
19
backend/.sqlx/query-c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e.json
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO metrics (id, value) VALUES ('author_count', $1), ('operator_count', $2)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c6b2791dd109c7bf40a40b557d9d5a140e70c49718fcb3a776b77f8f2ab901d4"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -27,6 +27,21 @@
|
||||
"ordinal": 4,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "summary",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -40,8 +55,11 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3911bf3bbc82d87366a5297496fd2350a252bcd69f1c7c972bf566ee7eb28b0a"
|
||||
"hash": "cb20f04352364f112ec564617722354577dc5f77169fd4573317454e47361d65"
|
||||
}
|
||||
15
backend/.sqlx/query-cccf9e216d84dfbb801b92d697496f44d93b344190a26c8393867b3a268b9aba.json
generated
Normal file
15
backend/.sqlx/query-cccf9e216d84dfbb801b92d697496f44d93b344190a26c8393867b3a268b9aba.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource (workspace_id, path, value, description, resource_type, created_by, edited_at) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "cccf9e216d84dfbb801b92d697496f44d93b344190a26c8393867b3a268b9aba"
|
||||
}
|
||||
16
backend/.sqlx/query-d1b508e1d8496e5e7aee9db555f9740128d87fe5183f9b7b72f550a5502e1cbb.json
generated
Normal file
16
backend/.sqlx/query-d1b508e1d8496e5e7aee9db555f9740128d87fe5183f9b7b72f550a5502e1cbb.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource_type SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d1b508e1d8496e5e7aee9db555f9740128d87fe5183f9b7b72f550a5502e1cbb"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description)\n VALUES ($1, $2, $3, $4)",
|
||||
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now())",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -8,10 +8,11 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text"
|
||||
"Text",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5061c0d054bf4f028e7fe51a8f9389024c6ae4492755cadac0f7167e5300bda0"
|
||||
"hash": "d249343a1d9fce2f7ca9e6e447eb32d7d11b61acdab80dda24db4a9ba3e1f7e6"
|
||||
}
|
||||
71
backend/.sqlx/query-d2def87d7f7901eebc65082f7df5e0a33e5702b25c3db3affa06155e90480e42.json
generated
Normal file
71
backend/.sqlx/query-d2def87d7f7901eebc65082f7df5e0a33e5702b25c3db3affa06155e90480e42.json
generated
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT * FROM job_perms WHERE job_id = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "job_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "is_admin",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "is_operator",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "groups",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "folders",
|
||||
"type_info": "JsonbArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d2def87d7f7901eebc65082f7df5e0a33e5702b25c3db3affa06155e90480e42"
|
||||
}
|
||||
15
backend/.sqlx/query-d323e898931b200eeec8c5608c37043ed1c47b6f6a53dbf562ad09ece29e82e4.json
generated
Normal file
15
backend/.sqlx/query-d323e898931b200eeec8c5608c37043ed1c47b6f6a53dbf562ad09ece29e82e4.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d323e898931b200eeec8c5608c37043ed1c47b6f6a53dbf562ad09ece29e82e4"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource (workspace_id, path, value, description, resource_type) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d956b7525f83e6d03beadc4bb3ee2798f53d990b01b17bdbc044719d4908e3f4"
|
||||
}
|
||||
22
backend/.sqlx/query-e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb.json
generated
Normal file
22
backend/.sqlx/query-e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT script_path FROM completed_job WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb"
|
||||
}
|
||||
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -31,6 +41,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
15
backend/.sqlx/query-f581f5ad5c386cef1037e3e8dcb759164e21a72ddc859304421ccb96233fa277.json
generated
Normal file
15
backend/.sqlx/query-f581f5ad5c386cef1037e3e8dcb759164e21a72ddc859304421ccb96233fa277.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f581f5ad5c386cef1037e3e8dcb759164e21a72ddc859304421ccb96233fa277"
|
||||
}
|
||||
15
backend/.sqlx/query-f9fed1aa8a4bbf1749da8c91b9cbae5605cd3ae7fd2e7ee774411971fa1ddc01.json
generated
Normal file
15
backend/.sqlx/query-f9fed1aa8a4bbf1749da8c91b9cbae5605cd3ae7fd2e7ee774411971fa1ddc01.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE schedule SET paused_until = NULL WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f9fed1aa8a4bbf1749da8c91b9cbae5605cd3ae7fd2e7ee774411971fa1ddc01"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource SET value = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
"query": "UPDATE resource SET value = $1, edited_at = now() WHERE path = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -12,5 +12,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8876fa929ffb175cd976a2bca1195704aa9fe7215013ae29e49ef15cb201ba57"
|
||||
"hash": "fd790067c02d384aead5ff8f94efb37cc20d2f7a3749d21a1de5642e888b5f42"
|
||||
}
|
||||
1122
backend/Cargo.lock
generated
1122
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.344.2"
|
||||
version = "1.355.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -24,7 +24,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.344.2"
|
||||
version = "1.355.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -159,19 +159,19 @@ tokio-util = { version = "^0", features = ["io"] }
|
||||
json-pointer = "^0"
|
||||
itertools = "^0"
|
||||
regex = "^1"
|
||||
deno_fetch = "0.179.0"
|
||||
deno_tls = "0.142.0"
|
||||
deno_console = "0.155.0"
|
||||
deno_url = "0.155.0"
|
||||
deno_webidl = "0.155.0"
|
||||
deno_web = "0.186.0"
|
||||
deno_net = "0.147.0"
|
||||
deno_core = "0.284.0"
|
||||
deno_ast = { version = "0.39.0", features = ["transpiling"] }
|
||||
deno_fetch = "0.182.0"
|
||||
deno_tls = "0.145.0"
|
||||
deno_console = "0.158.0"
|
||||
deno_url = "0.158.0"
|
||||
deno_webidl = "0.158.0"
|
||||
deno_web = "0.189.0"
|
||||
deno_net = "0.150.0"
|
||||
deno_core = "0.290.0"
|
||||
deno_ast = { version = "=0.39.2", features = ["transpiling"] }
|
||||
async-recursion = "^1"
|
||||
swc_common = "=0.33.26"
|
||||
swc_ecma_parser = "=0.144.1"
|
||||
swc_ecma_ast = "=0.113.4"
|
||||
swc_ecma_parser = "=0.144.3"
|
||||
swc_ecma_ast = "=0.113.7"
|
||||
swc_ecma_visit = "=0.99.1"
|
||||
base64 = "0.21.0"
|
||||
hmac = "0.12.1"
|
||||
@@ -213,7 +213,7 @@ wasm-bindgen-test = "0.3.0"
|
||||
convert_case = "0.6.0"
|
||||
getrandom = "0.2"
|
||||
tokio-postgres = {version = "^0.7", features = ["array-impls", "with-serde_json-1", "with-chrono-0_4", "with-uuid-1", "with-bit-vec-0_6"]}
|
||||
bit-vec = "0.6.3"
|
||||
bit-vec = "=0.6.3"
|
||||
mysql_async = { version = "*", default-features = false, features = ["minimal", "default", "native-tls-tls"]}
|
||||
postgres-native-tls = "^0"
|
||||
native-tls = "^0"
|
||||
@@ -235,8 +235,8 @@ tiberius = { git = "https://github.com/prisma/tiberius", rev = "8f66a699dfa041e7
|
||||
pin-project = "1"
|
||||
indexmap = { version = "2.2.5", features = ["serde"]}
|
||||
|
||||
datafusion = "38.0.0"
|
||||
object_store = { version = "0.9.1", features = ["aws", "azure"] }
|
||||
datafusion = "39.0.0"
|
||||
object_store = { version = "0.10.0", features = ["aws", "azure"] }
|
||||
openidconnect = { version = "3.4.0" }
|
||||
zstd = "=0.12.4"
|
||||
aws-config = "^1"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
fn main() {
|
||||
// trigger recompilation when a new migration is added
|
||||
println!("cargo:rerun-if-changed=migrations");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
50a2701b6bf16c55677066ed4aeeaf32cd5a66a8
|
||||
e9917f62ebc48bdd93f9ddda5a58ba6ce8a74ed9
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add up migration script here
|
||||
GRANT ALL ON outstanding_wait_time TO windmill_admin;
|
||||
GRANT ALL ON outstanding_wait_time TO windmill_user;
|
||||
1
backend/migrations/20240613150524_add_job_perms.down.sql
Normal file
1
backend/migrations/20240613150524_add_job_perms.down.sql
Normal file
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
13
backend/migrations/20240613150524_add_job_perms.up.sql
Normal file
13
backend/migrations/20240613150524_add_job_perms.up.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- Add up migration script here
|
||||
CREATE TABLE public.job_perms (
|
||||
job_id uuid NOT NULL,
|
||||
email varchar(255) NOT NULL,
|
||||
username varchar(50) NOT NULL,
|
||||
is_admin bool NOT NULL,
|
||||
is_operator bool NOT NULL,
|
||||
created_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
workspace_id varchar(50) NOT NULL,
|
||||
groups _text NOT NULL,
|
||||
folders _jsonb NOT NULL,
|
||||
CONSTRAINT job_perms_pk PRIMARY KEY (job_id)
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Add up migration script here
|
||||
GRANT ALL ON job_perms TO windmill_admin;
|
||||
GRANT ALL ON job_perms TO windmill_user;
|
||||
GRANT ALL ON job_logs TO windmill_admin;
|
||||
GRANT ALL ON job_logs TO windmill_user;
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
4
backend/migrations/20240619165014_extra_metadata.up.sql
Normal file
4
backend/migrations/20240619165014_extra_metadata.up.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- Add up migration script here
|
||||
alter table resource add column edited_at timestamptz, add column created_by varchar(50);
|
||||
alter table resource_type add column edited_at timestamptz, add column created_by varchar(50);
|
||||
alter table folder add column summary text, add column edited_at timestamptz, add column created_by varchar(50);
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add up migration script here
|
||||
alter table schedule add column paused_until timestamptz;
|
||||
@@ -56,7 +56,7 @@ static PYTHON_IMPORTS_REPLACEMENT: phf::Map<&'static str, &'static str> = phf_ma
|
||||
"ldap" => "python-ldap",
|
||||
"opensearchpy" => "opensearch-py",
|
||||
"lokalise" => "python-lokalise-api",
|
||||
|
||||
"msgraph" => "msgraph-sdk",
|
||||
};
|
||||
|
||||
fn replace_import(x: String) -> String {
|
||||
|
||||
@@ -316,6 +316,7 @@ pub fn parse_pg_typ(typ: &str) -> Typ {
|
||||
| "timestamptz"
|
||||
| "timestamp with time zone"
|
||||
| "timestamp without time zone" => Typ::Datetime,
|
||||
"bytea" => Typ::Bytes,
|
||||
_ => Typ::Str(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use regex::Regex;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashSet;
|
||||
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
|
||||
use windmill_parser::{json_to_typ, Arg, MainArgSignature, ObjectProperty, Typ};
|
||||
use windmill_parser::{json_to_typ, Arg, MainArgSignature, ObjectProperty, OneOfVariant, Typ};
|
||||
|
||||
use swc_common::{sync::Lrc, FileName, SourceMap, SourceMapper, Span, Spanned};
|
||||
use swc_ecma_ast::{
|
||||
@@ -401,6 +401,79 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
|
||||
};
|
||||
(tstype_to_typ(&types[other_p]).0, true)
|
||||
} else {
|
||||
if types.len() > 1 {
|
||||
let one_of_values: Vec<OneOfVariant> = types
|
||||
.into_iter()
|
||||
.map_while(|x| match &**x {
|
||||
TsType::TsTypeLit(TsTypeLit { members, .. }) => {
|
||||
let label = members
|
||||
.iter()
|
||||
.find_map(|y| match y {
|
||||
TsTypeElement::TsPropertySignature(
|
||||
TsPropertySignature { key, type_ann, .. },
|
||||
) => match (&**key, type_ann) {
|
||||
(Expr::Ident(Ident { sym, .. }), type_ann) => {
|
||||
if sym.to_string() == "label" {
|
||||
type_ann.as_ref().map(|z| match &*z.type_ann {
|
||||
TsType::TsLitType(TsLitType {
|
||||
lit: TsLit::Str(Str { value, .. }),
|
||||
..
|
||||
}) => Some(value.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.flatten();
|
||||
|
||||
match label {
|
||||
Some(label) => {
|
||||
let properties: Vec<ObjectProperty> = members
|
||||
.into_iter()
|
||||
.filter_map(|x| match x {
|
||||
TsTypeElement::TsPropertySignature(
|
||||
TsPropertySignature { key, type_ann, .. },
|
||||
) => match (*key.to_owned(), type_ann) {
|
||||
(Expr::Ident(Ident { sym, .. }), type_ann) => {
|
||||
Some(ObjectProperty {
|
||||
key: sym.to_string(),
|
||||
typ: type_ann
|
||||
.as_ref()
|
||||
.map(|typ| {
|
||||
Box::new(
|
||||
tstype_to_typ(
|
||||
&*typ.type_ann,
|
||||
)
|
||||
.0,
|
||||
)
|
||||
})
|
||||
.unwrap_or(Box::new(Typ::Unknown)),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
Some(OneOfVariant { label, properties })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
if one_of_values.len() == types.len() {
|
||||
return (Typ::OneOf(one_of_values), false);
|
||||
}
|
||||
}
|
||||
|
||||
let literals = types
|
||||
.into_iter()
|
||||
.filter(|x| match ***x {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"collaborators": [
|
||||
"Ruben Fiszel <ruben@windmill.dev>"
|
||||
],
|
||||
"version": "1.335.0",
|
||||
"version": "1.347.1",
|
||||
"files": [
|
||||
"windmill_parser_wasm_bg.wasm",
|
||||
"windmill_parser_wasm.js",
|
||||
|
||||
@@ -97,6 +97,15 @@ function getInt32Memory0() {
|
||||
return cachedInt32Memory0;
|
||||
}
|
||||
|
||||
let cachedFloat64Memory0 = null;
|
||||
|
||||
function getFloat64Memory0() {
|
||||
if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) {
|
||||
cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedFloat64Memory0;
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
@@ -115,15 +124,6 @@ function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
let cachedFloat64Memory0 = null;
|
||||
|
||||
function getFloat64Memory0() {
|
||||
if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) {
|
||||
cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedFloat64Memory0;
|
||||
}
|
||||
|
||||
let cachedBigInt64Memory0 = null;
|
||||
|
||||
function getBigInt64Memory0() {
|
||||
@@ -604,6 +604,21 @@ function __wbg_get_imports() {
|
||||
const ret = typeof(getObject(arg0)) === 'bigint';
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_is_object = function(arg0) {
|
||||
const val = getObject(arg0);
|
||||
const ret = typeof(val) === 'object' && val !== null;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_in = function(arg0, arg1) {
|
||||
const ret = getObject(arg0) in getObject(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_bigint_from_i64 = function(arg0) {
|
||||
const ret = arg0;
|
||||
return addHeapObject(ret);
|
||||
@@ -620,22 +635,7 @@ function __wbg_get_imports() {
|
||||
const ret = new Error(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_is_object = function(arg0) {
|
||||
const val = getObject(arg0);
|
||||
const ret = typeof(val) === 'object' && val !== null;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_in = function(arg0, arg1) {
|
||||
const ret = getObject(arg0) in getObject(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_eval_4ecb84501b3abc94 = function(arg0, arg1) {
|
||||
imports.wbg.__wbg_eval_83a1cdd46625140c = function(arg0, arg1) {
|
||||
const ret = eval(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
Binary file not shown.
@@ -24,6 +24,13 @@ pub struct ObjectProperty {
|
||||
pub typ: Box<Typ>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all(serialize = "lowercase"))]
|
||||
pub struct OneOfVariant {
|
||||
pub label: String,
|
||||
pub properties: Vec<ObjectProperty>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all(serialize = "lowercase"))]
|
||||
pub enum Typ {
|
||||
@@ -38,6 +45,7 @@ pub enum Typ {
|
||||
Email,
|
||||
Sql,
|
||||
Object(Vec<ObjectProperty>),
|
||||
OneOf(Vec<OneOfVariant>),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,11 @@ use windmill_common::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ERROR_CHANNELS_SETTING,
|
||||
CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, ENV_SETTINGS,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, KEEP_JOB_DIR_SETTING,
|
||||
LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING, PIP_INDEX_URL_SETTING,
|
||||
REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING,
|
||||
RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING,
|
||||
HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING,
|
||||
PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING,
|
||||
},
|
||||
stats_ee::schedule_stats,
|
||||
utils::{rd_string, Mode},
|
||||
@@ -53,9 +54,9 @@ use windmill_common::METRICS_ADDR;
|
||||
use windmill_common::global_settings::OBJECT_STORE_CACHE_CONFIG_SETTING;
|
||||
|
||||
use windmill_worker::{
|
||||
BUN_CACHE_DIR, DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM, GO_BIN_CACHE_DIR,
|
||||
GO_CACHE_DIR, HUB_CACHE_DIR, LOCK_CACHE_DIR, PIP_CACHE_DIR, POWERSHELL_CACHE_DIR,
|
||||
TAR_PIP_CACHE_DIR, TMP_LOGS_DIR,
|
||||
BUN_CACHE_DIR, BUN_TAR_CACHE_DIR, DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM,
|
||||
GO_BIN_CACHE_DIR, GO_CACHE_DIR, HUB_CACHE_DIR, LOCK_CACHE_DIR, PIP_CACHE_DIR,
|
||||
POWERSHELL_CACHE_DIR, TAR_PIP_CACHE_DIR, TMP_LOGS_DIR,
|
||||
};
|
||||
|
||||
use crate::monitor::{
|
||||
@@ -63,9 +64,10 @@ use crate::monitor::{
|
||||
load_tag_per_workspace_enabled, monitor_db, monitor_pool, reload_base_url_setting,
|
||||
reload_bunfig_install_scopes_setting, reload_critical_error_channels_setting,
|
||||
reload_extra_pip_index_url_setting, reload_hub_base_url_setting,
|
||||
reload_job_default_timeout_setting, reload_license_key, reload_npm_config_registry_setting,
|
||||
reload_pip_index_url_setting, reload_retention_period_setting, reload_scim_token_setting,
|
||||
reload_server_config, reload_worker_config,
|
||||
reload_job_default_timeout_setting, reload_jwt_secret_setting, reload_license_key,
|
||||
reload_npm_config_registry_setting, reload_pip_index_url_setting,
|
||||
reload_retention_period_setting, reload_scim_token_setting, reload_server_config,
|
||||
reload_worker_config,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -163,7 +165,7 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
panic!("BASE_INTERNAL_URL is required in agent mode")
|
||||
}
|
||||
if std::env::var("JOB_TOKEN").is_err() {
|
||||
tracing::warn!("JOB_TOKEN is not passed, hence workers will still create one ephemeral token per job and the DATABASE_URL need to be of a role that can INSERT into the token table")
|
||||
tracing::warn!("JOB_TOKEN is not passed, hence workers will still need to create permissions for each job and the DATABASE_URL needs to be of a role that can INSERT into the job_perms table")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
@@ -536,6 +538,11 @@ Windmill Community Edition {GIT_VERSION}
|
||||
tracing::error!(error = %e, "Could not reload critical error emails setting");
|
||||
}
|
||||
},
|
||||
JWT_SECRET_SETTING => {
|
||||
if let Err(e) = reload_jwt_secret_setting(&db).await {
|
||||
tracing::error!(error = %e, "Could not reload jwt secret setting");
|
||||
}
|
||||
},
|
||||
a @_ => {
|
||||
tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a);
|
||||
}
|
||||
@@ -709,6 +716,7 @@ pub async fn run_workers<R: rsmq_async::RsmqConnection + Send + Sync + Clone + '
|
||||
DENO_CACHE_DIR_DEPS,
|
||||
DENO_CACHE_DIR_NPM,
|
||||
BUN_CACHE_DIR,
|
||||
BUN_TAR_CACHE_DIR,
|
||||
GO_CACHE_DIR,
|
||||
GO_BIN_CACHE_DIR,
|
||||
HUB_CACHE_DIR,
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use rsmq_async::MultiplexedRsmq;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::de::DeserializeOwned;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tokio::{
|
||||
join,
|
||||
@@ -22,18 +22,30 @@ use windmill_api::{
|
||||
DEFAULT_BODY_LIMIT, IS_SECURE, OAUTH_CLIENTS, REQUEST_SIZE_LIMIT, SAML_METADATA, SCIM_TOKEN,
|
||||
};
|
||||
use windmill_common::{
|
||||
ee::CriticalErrorChannel, error, flow_status::FlowStatusModule, global_settings::{
|
||||
auth::JWT_SECRET,
|
||||
ee::CriticalErrorChannel,
|
||||
error,
|
||||
flow_status::FlowStatusModule,
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ERROR_CHANNELS_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
|
||||
EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING,
|
||||
PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
OAUTH_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING,
|
||||
}, jobs::QueuedJob, oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH, server::load_server_config, stats_ee::get_user_usage, users::truncate_token, utils::now_from_db, worker::{
|
||||
},
|
||||
jobs::QueuedJob,
|
||||
oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH,
|
||||
server::load_server_config,
|
||||
users::truncate_token,
|
||||
utils::{now_from_db, rd_string},
|
||||
worker::{
|
||||
load_worker_config, reload_custom_tags_setting, DEFAULT_TAGS_PER_WORKSPACE, SERVER_CONFIG,
|
||||
WORKER_CONFIG,
|
||||
}, BASE_URL, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, METRICS_DEBUG_ENABLED, METRICS_ENABLED
|
||||
},
|
||||
BASE_URL, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL,
|
||||
METRICS_DEBUG_ENABLED, METRICS_ENABLED,
|
||||
};
|
||||
use windmill_queue::cancel_job;
|
||||
use windmill_worker::{
|
||||
@@ -140,6 +152,10 @@ pub async fn initial_load(
|
||||
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);
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
if !_is_agent {
|
||||
reload_s3_cache_setting(&db).await;
|
||||
@@ -200,7 +216,7 @@ pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> {
|
||||
tracing::error!("Error setting jemalloc prof_active: {e:?}");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
@@ -208,7 +224,9 @@ pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> {
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MallctlError { pub code: i32 }
|
||||
pub struct MallctlError {
|
||||
pub code: i32,
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
fn set_prof_active(new_value: bool) -> Result<(), MallctlError> {
|
||||
@@ -216,13 +234,12 @@ fn set_prof_active(new_value: bool) -> Result<(), MallctlError> {
|
||||
|
||||
tracing::info!("Setting jemalloc prof_active to {}", new_value);
|
||||
let result = unsafe {
|
||||
|
||||
tikv_jemalloc_sys::mallctl(
|
||||
option_name.as_ptr(), // const char *name
|
||||
std::ptr::null_mut(), // void *oldp
|
||||
std::ptr::null_mut(), // size_t *oldlenp
|
||||
&new_value as *const _ as *mut _, // void *newp
|
||||
std::mem::size_of_val(&new_value) // size_t newlen
|
||||
option_name.as_ptr(), // const char *name
|
||||
std::ptr::null_mut(), // void *oldp
|
||||
std::ptr::null_mut(), // size_t *oldlenp
|
||||
&new_value as *const _ as *mut _, // void *newp
|
||||
std::mem::size_of_val(&new_value), // size_t newlen
|
||||
)
|
||||
};
|
||||
|
||||
@@ -239,15 +256,12 @@ pub fn bytes_to_mb(bytes: u64) -> f64 {
|
||||
bytes as f64 / BYTES_PER_MB
|
||||
}
|
||||
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
pub async fn monitor_mem() {
|
||||
|
||||
use std::time::Duration;
|
||||
use tikv_jemalloc_ctl::{stats, epoch};
|
||||
use tikv_jemalloc_ctl::{epoch, stats};
|
||||
|
||||
tokio::spawn(async move {
|
||||
|
||||
// Obtain a MIB for the `epoch`, `stats.allocated`, and
|
||||
// `atats.resident` keys:
|
||||
let e = match epoch::mib() {
|
||||
@@ -270,19 +284,22 @@ pub async fn monitor_mem() {
|
||||
tracing::error!("Error getting jemalloc resident mib: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
loop {
|
||||
// Many statistics are cached and only updated
|
||||
// Many statistics are cached and only updated
|
||||
// when the epoch is advanced:
|
||||
match e.advance() {
|
||||
Ok(_) => {
|
||||
// Read statistics using MIB key:
|
||||
let allocated = allocated.read().unwrap_or_default();
|
||||
let resident = resident.read().unwrap_or_default();
|
||||
tracing::info!("{} mb allocated/{} mb resident", bytes_to_mb(allocated as u64), bytes_to_mb(resident as u64));
|
||||
},
|
||||
tracing::info!(
|
||||
"{} mb allocated/{} mb resident",
|
||||
bytes_to_mb(allocated as u64),
|
||||
bytes_to_mb(resident as u64)
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error advancing jemalloc epoch: {:?}", e);
|
||||
}
|
||||
@@ -768,17 +785,10 @@ pub async fn monitor_db(
|
||||
}
|
||||
};
|
||||
|
||||
let save_usage_metrics_f = async {
|
||||
if !initial_load && server_mode {
|
||||
save_usage_metrics(&db).await;
|
||||
}
|
||||
};
|
||||
|
||||
join!(
|
||||
expired_items_f,
|
||||
zombie_jobs_f,
|
||||
expose_queue_metrics_f,
|
||||
save_usage_metrics_f,
|
||||
verify_license_key_f
|
||||
);
|
||||
}
|
||||
@@ -799,7 +809,6 @@ 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
|
||||
@@ -845,91 +854,13 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
|
||||
// clean queue metrics older than 14 days
|
||||
sqlx::query!(
|
||||
"DELETE FROM metrics WHERE id LIKE 'queue_%' AND created_at < NOW() - INTERVAL '14 day'"
|
||||
).execute(&mut *tx).await.ok();
|
||||
|
||||
tx.commit().await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WorkerUsage {
|
||||
worker: String,
|
||||
worker_instance: String,
|
||||
vcpus: Option<i64>,
|
||||
memory: Option<i64>,
|
||||
|
||||
}
|
||||
|
||||
pub async fn save_usage_metrics(db: &Pool<Postgres>) {
|
||||
let tx = db.begin().await;
|
||||
|
||||
if let Ok(mut tx) = tx {
|
||||
let last_check = sqlx::query_scalar!(
|
||||
"SELECT created_at FROM metrics WHERE id = 'author_count' ORDER BY created_at DESC LIMIT 1"
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.unwrap_or(Some(chrono::Utc::now()));
|
||||
|
||||
let random_nb = rand::random::<i64>();
|
||||
|
||||
// save author and operator count every ~24 hours
|
||||
if last_check
|
||||
.map(|last_check| chrono::Utc::now() - last_check > chrono::Duration::hours(24) - chrono::Duration::minutes(random_nb % 60))
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let user_usage = get_user_usage(&mut *tx).await.ok();
|
||||
|
||||
if let Some(user_usage) = user_usage {
|
||||
sqlx::query!(
|
||||
"INSERT INTO metrics (id, value) VALUES ('author_count', $1), ('operator_count', $2)",
|
||||
serde_json::json!(user_usage.author_count.unwrap_or(0)),
|
||||
serde_json::json!(user_usage.operator_count.unwrap_or(0))
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
|
||||
// clean metrics older than 6 months (including worker usage)
|
||||
sqlx::query!(
|
||||
"DELETE FROM metrics
|
||||
WHERE (id = 'author_count' OR id = 'operator_count' OR id = 'worker_usage') AND created_at < NOW() - INTERVAL '6 month'"
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
// save worker usage every ~60 minutes
|
||||
if last_check
|
||||
.map(|last_check| chrono::Utc::now() - last_check > chrono::Duration::minutes(60) - chrono::Duration::seconds(random_nb % 300))
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let worker_usage = sqlx::query_as!(
|
||||
WorkerUsage,
|
||||
"SELECT worker, worker_instance, vcpus, memory FROM worker_ping WHERE ping_at > NOW() - INTERVAL '2 minutes'"
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
if let Some(worker_usage) = worker_usage {
|
||||
sqlx::query!(
|
||||
"INSERT INTO metrics (id, value) VALUES ('worker_usage', $1)",
|
||||
serde_json::json!(worker_usage)
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
.ok();
|
||||
|
||||
tx.commit().await.ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub async fn reload_server_config(db: &Pool<Postgres>) {
|
||||
@@ -1103,7 +1034,9 @@ async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
mpsc::channel::<SameWorkerPayload>(1);
|
||||
let (send_result_never_used, _send_result_rx_never_used) = mpsc::channel::<SendResult>(1);
|
||||
|
||||
let label = if job.permissioned_as != format!("u/{}", job.created_by) && job.permissioned_as != job.created_by {
|
||||
let label = if job.permissioned_as != format!("u/{}", job.created_by)
|
||||
&& job.permissioned_as != job.created_by
|
||||
{
|
||||
format!("ephemeral-script-end-user-{}", job.created_by)
|
||||
} else {
|
||||
"ephemeral-script".to_string()
|
||||
@@ -1161,7 +1094,7 @@ async fn handle_zombie_flows(
|
||||
SELECT *
|
||||
FROM queue
|
||||
WHERE running = true AND suspend = 0 AND suspend_until IS null AND scheduled_for <= now() AND (job_kind = 'flow' OR job_kind = 'flowpreview')
|
||||
AND last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval
|
||||
AND last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval AND canceled = false
|
||||
"#,
|
||||
).bind(FLOW_ZOMBIE_TRANSITION_TIMEOUT.as_str())
|
||||
.fetch_all(db)
|
||||
@@ -1181,7 +1114,7 @@ async fn handle_zombie_flows(
|
||||
);
|
||||
// if the flow hasn't started and is a zombie, we can simply restart it
|
||||
sqlx::query!(
|
||||
"UPDATE queue SET running = false, started_at = null WHERE id = $1",
|
||||
"UPDATE queue SET running = false, started_at = null WHERE id = $1 AND canceled = false",
|
||||
flow.id
|
||||
)
|
||||
.execute(db)
|
||||
@@ -1213,11 +1146,12 @@ async fn handle_zombie_flows(
|
||||
.await?;
|
||||
|
||||
for flow in flows2 {
|
||||
let in_queue =
|
||||
sqlx::query_as::<_, QueuedJob>("SELECT * FROM queue WHERE id = $1 AND running = true")
|
||||
.bind(flow.parent_flow_id)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
let in_queue = sqlx::query_as::<_, QueuedJob>(
|
||||
"SELECT * FROM queue WHERE id = $1 AND running = true AND canceled = false",
|
||||
)
|
||||
.bind(flow.parent_flow_id)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
if let Some(job) = in_queue {
|
||||
tracing::error!(
|
||||
"parallel Zombie flow detected: {} in workspace {}. Last ping was: {:?}.",
|
||||
@@ -1247,7 +1181,7 @@ async fn cancel_zombie_flow_job(
|
||||
flow.id,
|
||||
flow.workspace_id
|
||||
);
|
||||
let (mut ntx, _) = cancel_job(
|
||||
let (ntx, _) = cancel_job(
|
||||
"monitor",
|
||||
Some(message),
|
||||
flow.id,
|
||||
@@ -1256,15 +1190,9 @@ async fn cancel_zombie_flow_job(
|
||||
db,
|
||||
rsmq.clone(),
|
||||
false,
|
||||
false
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"UPDATE queue SET running = false, started_at = null WHERE id = $1",
|
||||
flow.id
|
||||
)
|
||||
.execute(&mut *ntx)
|
||||
.await?;
|
||||
ntx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1334,3 +1262,35 @@ pub async fn reload_critical_error_channels_setting(db: &DB) -> error::Result<()
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn generate_and_save_jwt_secret(db: &DB) -> error::Result<String> {
|
||||
let secret = rd_string(32);
|
||||
sqlx::query!(
|
||||
"INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2",
|
||||
JWT_SECRET_SETTING,
|
||||
serde_json::to_value(&secret).unwrap()
|
||||
).execute(db).await?;
|
||||
|
||||
Ok(secret)
|
||||
}
|
||||
|
||||
pub async fn reload_jwt_secret_setting(db: &DB) -> error::Result<()> {
|
||||
let jwt_secret = load_value_from_global_settings(db, JWT_SECRET_SETTING).await?;
|
||||
|
||||
let jwt_secret = if let Some(q) = jwt_secret {
|
||||
if let Ok(v) = serde_json::from_value::<String>(q.clone()) {
|
||||
v
|
||||
} else {
|
||||
tracing::error!("Could not parse jwt_secret setting, generating new one");
|
||||
generate_and_save_jwt_secret(db).await?
|
||||
}
|
||||
} else {
|
||||
tracing::info!("Not jwt secret found, generating one");
|
||||
generate_and_save_jwt_secret(db).await?
|
||||
};
|
||||
|
||||
let mut l = JWT_SECRET.write().await;
|
||||
*l = jwt_secret;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use windmill_api_client::types::{NewScript, NewScriptLanguage};
|
||||
use std::str::FromStr;
|
||||
use windmill_api_client::types::{NewScript, NewScriptLanguage};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use chrono::Timelike;
|
||||
@@ -13,26 +13,23 @@ use sqlx::{postgres::PgListener, types::Uuid, Pool, Postgres};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use tokio::time::{timeout, Duration};
|
||||
|
||||
use windmill_api_client::types::{
|
||||
CreateFlowBody, RawScript
|
||||
};
|
||||
use windmill_api_client::types::{CreateFlowBody, RawScript};
|
||||
|
||||
use sqlx::query;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_api_client::types::{EditSchedule, NewSchedule, ScriptArgs};
|
||||
|
||||
|
||||
use windmill_common::worker::{WORKER_CONFIG, PriorityTags};
|
||||
use serde::Serialize;
|
||||
use windmill_common::auth::JWT_SECRET;
|
||||
use windmill_common::worker::{PriorityTags, WORKER_CONFIG};
|
||||
use windmill_common::{
|
||||
flow_status::{FlowStatus, FlowStatusModule, RestartedFrom},
|
||||
flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform},
|
||||
jobs::{JobPayload, RawCode, JobKind},
|
||||
scripts::{ScriptLang, ScriptHash}
|
||||
jobs::{JobKind, JobPayload, RawCode},
|
||||
scripts::{ScriptHash, ScriptLang},
|
||||
};
|
||||
use windmill_queue::PushIsolationLevel;
|
||||
use serde::Serialize;
|
||||
|
||||
|
||||
#[derive(Debug, sqlx::FromRow, Serialize)]
|
||||
pub struct CompletedJob {
|
||||
@@ -66,7 +63,6 @@ pub struct CompletedJob {
|
||||
pub script_hash: Option<ScriptHash>,
|
||||
pub language: Option<ScriptLang>,
|
||||
pub job_kind: JobKind,
|
||||
|
||||
}
|
||||
|
||||
impl CompletedJob {
|
||||
@@ -168,6 +164,12 @@ fn find_module_in_vec(modules: Vec<FlowStatusModule>, id: &str) -> Option<FlowSt
|
||||
modules.into_iter().find(|s| s.id() == id)
|
||||
}
|
||||
|
||||
async fn set_jwt_secret() -> () {
|
||||
let secret = "mytestsecret".to_string();
|
||||
let mut l = JWT_SECRET.write().await;
|
||||
*l = secret;
|
||||
}
|
||||
|
||||
mod suspend_resume {
|
||||
|
||||
use serde_json::json;
|
||||
@@ -272,11 +274,12 @@ mod suspend_resume {
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
let flow = RunJob::from(JobPayload::RawFlow { value: flow(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.arg("port", json!(port))
|
||||
.push(&db)
|
||||
.await;
|
||||
let flow =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.arg("port", json!(port))
|
||||
.push(&db)
|
||||
.await;
|
||||
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
let queue = listen_for_queue(&db).await;
|
||||
@@ -353,14 +356,15 @@ mod suspend_resume {
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.arg("op", json!("cancel"))
|
||||
.arg("port", json!(port))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.arg("op", json!("cancel"))
|
||||
.arg("port", json!(port))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
server.close().await.unwrap();
|
||||
|
||||
@@ -377,11 +381,12 @@ mod suspend_resume {
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
let flow = RunJob::from(JobPayload::RawFlow { value: flow(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.arg("port", json!(port))
|
||||
.push(&db)
|
||||
.await;
|
||||
let flow =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.arg("port", json!(port))
|
||||
.push(&db)
|
||||
.await;
|
||||
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
let queue = listen_for_queue(&db).await;
|
||||
@@ -571,13 +576,17 @@ def main(last, port):
|
||||
.into_iter()
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
let server = Server::start(responses).await;
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow_forloop_retry(), path: None, restarted_from: None })
|
||||
.arg("items", json!(["unused", "unused", "unused"]))
|
||||
.arg("port", json!(server.addr.port()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result = RunJob::from(JobPayload::RawFlow {
|
||||
value: flow_forloop_retry(),
|
||||
path: None,
|
||||
restarted_from: None,
|
||||
})
|
||||
.arg("items", json!(["unused", "unused", "unused"]))
|
||||
.arg("port", json!(server.addr.port()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(server.close().await, attempts);
|
||||
assert_eq!(json!([3, 5, 7, 9]), result);
|
||||
@@ -600,13 +609,17 @@ def main(last, port):
|
||||
.into_iter()
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
let server = Server::start(responses).await;
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow_forloop_retry(), path: None, restarted_from: None })
|
||||
.arg("items", json!(["unused", "unused", "unused"]))
|
||||
.arg("port", json!(server.addr.port()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result = RunJob::from(JobPayload::RawFlow {
|
||||
value: flow_forloop_retry(),
|
||||
path: None,
|
||||
restarted_from: None,
|
||||
})
|
||||
.arg("items", json!(["unused", "unused", "unused"]))
|
||||
.arg("port", json!(server.addr.port()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(server.close().await, attempts);
|
||||
|
||||
@@ -641,11 +654,15 @@ def main(last, port):
|
||||
.into_iter()
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
let server = Server::start(responses).await;
|
||||
let job = RunJob::from(JobPayload::RawFlow { value: flow_forloop_retry(), path: None, restarted_from: None })
|
||||
.arg("items", json!(["unused", "unused", "unused"]))
|
||||
.arg("port", json!(server.addr.port()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await;
|
||||
let job = RunJob::from(JobPayload::RawFlow {
|
||||
value: flow_forloop_retry(),
|
||||
path: None,
|
||||
restarted_from: None,
|
||||
})
|
||||
.arg("items", json!(["unused", "unused", "unused"]))
|
||||
.arg("port", json!(server.addr.port()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await;
|
||||
|
||||
let result = job.json_result().unwrap();
|
||||
assert_eq!(server.close().await, attempts);
|
||||
@@ -765,21 +782,23 @@ async fn test_iteration(db: Pool<Postgres>) {
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!([]))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!([]))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
assert_eq!(result, serde_json::json!([]));
|
||||
|
||||
/* Don't actually test that this does 257 jobs or that will take forever. */
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!((0..257).collect::<Vec<_>>()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!((0..257).collect::<Vec<_>>()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
assert!(matches!(result, serde_json::Value::Array(_)));
|
||||
assert!(result[2]["error"]
|
||||
.as_object()
|
||||
@@ -822,19 +841,21 @@ async fn test_iteration_parallel(db: Pool<Postgres>) {
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!([]))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!([]))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
assert_eq!(result, serde_json::json!([]));
|
||||
|
||||
/* Don't actually test that this does 257 jobs or that will take forever. */
|
||||
let job = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!((0..50).collect::<Vec<_>>()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await;
|
||||
let job =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("items", json!((0..50).collect::<Vec<_>>()))
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await;
|
||||
// println!("{:#?}", job);
|
||||
let result = job.json_result().unwrap();
|
||||
assert!(matches!(result, serde_json::Value::Array(_)));
|
||||
@@ -870,7 +891,8 @@ impl RunJob {
|
||||
let mut hm_args = std::collections::HashMap::new();
|
||||
for (k, v) in args {
|
||||
hm_args.insert(k, windmill_common::worker::to_raw_value(&v));
|
||||
}
|
||||
}
|
||||
|
||||
let tx = PushIsolationLevel::IsolatedRoot(db.clone(), None);
|
||||
let (uuid, tx) = windmill_queue::push::<rsmq_async::MultiplexedRsmq>(
|
||||
&db,
|
||||
@@ -894,6 +916,7 @@ impl RunJob {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("push has to succeed");
|
||||
@@ -928,6 +951,7 @@ async fn in_test_worker<Fut: std::future::Future>(
|
||||
inner: Fut,
|
||||
port: u16,
|
||||
) -> <Fut as std::future::Future>::Output {
|
||||
set_jwt_secret().await;
|
||||
let (quit, worker) = spawn_test_worker(db, port);
|
||||
let worker = tokio::time::timeout(std::time::Duration::from_secs(60), worker);
|
||||
tokio::pin!(worker);
|
||||
@@ -959,7 +983,10 @@ fn spawn_test_worker(
|
||||
tokio::sync::broadcast::Sender<()>,
|
||||
tokio::task::JoinHandle<()>,
|
||||
) {
|
||||
for x in [windmill_worker::LOCK_CACHE_DIR, windmill_worker::GO_BIN_CACHE_DIR] {
|
||||
for x in [
|
||||
windmill_worker::LOCK_CACHE_DIR,
|
||||
windmill_worker::GO_BIN_CACHE_DIR,
|
||||
] {
|
||||
std::fs::DirBuilder::new()
|
||||
.recursive(true)
|
||||
.create(x)
|
||||
@@ -976,9 +1003,10 @@ fn spawn_test_worker(
|
||||
let future = async move {
|
||||
let base_internal_url = format!("http://localhost:{}", port);
|
||||
{
|
||||
let mut wc = WORKER_CONFIG.write().await;
|
||||
(*wc).worker_tags = windmill_common::worker::DEFAULT_TAGS.clone();
|
||||
(*wc).priority_tags_sorted = vec![PriorityTags { priority: 0, tags: (*wc).worker_tags.clone()} ]
|
||||
let mut wc = WORKER_CONFIG.write().await;
|
||||
(*wc).worker_tags = windmill_common::worker::DEFAULT_TAGS.clone();
|
||||
(*wc).priority_tags_sorted =
|
||||
vec![PriorityTags { priority: 0, tags: (*wc).worker_tags.clone() }]
|
||||
}
|
||||
windmill_worker::run_worker::<rsmq_async::MultiplexedRsmq>(
|
||||
&db,
|
||||
@@ -1028,11 +1056,13 @@ async fn listen_for_uuid_on(
|
||||
}
|
||||
|
||||
async fn completed_job(uuid: Uuid, db: &Pool<Postgres>) -> CompletedJob {
|
||||
|
||||
sqlx::query_as::<_, CompletedJob>("SELECT *, result->'wm_labels' as labels FROM completed_job WHERE id = $1").bind(uuid)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.unwrap()
|
||||
sqlx::query_as::<_, CompletedJob>(
|
||||
"SELECT *, result->'wm_labels' as labels FROM completed_job WHERE id = $1",
|
||||
)
|
||||
.bind(uuid)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[axum::async_trait(?Send)]
|
||||
@@ -1073,7 +1103,8 @@ async fn test_deno_flow(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
}.into(),
|
||||
}
|
||||
.into(),
|
||||
stop_after_if: Default::default(),
|
||||
summary: Default::default(),
|
||||
suspend: Default::default(),
|
||||
@@ -1111,7 +1142,8 @@ async fn test_deno_flow(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
}.into(),
|
||||
}
|
||||
.into(),
|
||||
stop_after_if: Default::default(),
|
||||
summary: Default::default(),
|
||||
suspend: Default::default(),
|
||||
@@ -1124,7 +1156,8 @@ async fn test_deno_flow(db: Pool<Postgres>) {
|
||||
delete_after_use: None,
|
||||
continue_on_error: None,
|
||||
}],
|
||||
}.into(),
|
||||
}
|
||||
.into(),
|
||||
stop_after_if: Default::default(),
|
||||
summary: Default::default(),
|
||||
suspend: Default::default(),
|
||||
@@ -1183,11 +1216,12 @@ async fn test_identity(db: Pool<Postgres>) {
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.run_until_complete(&db, server.addr.port())
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
assert_eq!(result, serde_json::json!(42));
|
||||
}
|
||||
|
||||
@@ -1681,7 +1715,7 @@ func main(derp string) (string, error) {
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None
|
||||
dedicated_worker: None,
|
||||
}))
|
||||
.arg("derp", json!("world"))
|
||||
.run_until_complete(&db, port)
|
||||
@@ -1714,7 +1748,7 @@ echo "hello $msg"
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None
|
||||
dedicated_worker: None,
|
||||
}))
|
||||
.arg("msg", json!("world"))
|
||||
.run_until_complete(&db, port)
|
||||
@@ -1744,7 +1778,7 @@ def main():
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None
|
||||
dedicated_worker: None,
|
||||
});
|
||||
|
||||
let result = run_job_in_new_worker_until_complete(&db, job, port)
|
||||
@@ -1780,7 +1814,7 @@ def main():
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None
|
||||
dedicated_worker: None,
|
||||
});
|
||||
|
||||
let result = run_job_in_new_worker_until_complete(&db, job, port)
|
||||
@@ -1815,7 +1849,7 @@ def main():
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None
|
||||
dedicated_worker: None,
|
||||
});
|
||||
|
||||
let result = run_job_in_new_worker_until_complete(&db, job, port)
|
||||
@@ -1915,9 +1949,8 @@ async fn test_invalid_first_step(db: Pool<Postgres>) {
|
||||
let flow = JobPayload::RawFlow { value: flow, path: None, restarted_from: None };
|
||||
let job = run_job_in_new_worker_until_complete(&db, flow, port).await;
|
||||
|
||||
assert_eq!(
|
||||
job.json_result().unwrap(),
|
||||
serde_json::json!( {"error": {"name": "InternalErr", "message": "Expected an array value in the iterator expression, found: invalid type: map, expected a sequence at line 1 column 0"}})
|
||||
assert!(
|
||||
serde_json::to_string(&job.json_result().unwrap()).unwrap().contains("Expected an array value in the iterator expression, found: invalid type: map, expected a sequence at line 1 column 0")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2457,12 +2490,13 @@ async fn test_failure_module(db: Pool<Postgres>) {
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(0))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(0))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert!(result["from failure module"]["error"]
|
||||
.as_object()
|
||||
@@ -2473,12 +2507,13 @@ async fn test_failure_module(db: Pool<Postgres>) {
|
||||
.unwrap()
|
||||
.contains("[]"));
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(1))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert!(result["from failure module"]["error"]
|
||||
.as_object()
|
||||
@@ -2489,12 +2524,13 @@ async fn test_failure_module(db: Pool<Postgres>) {
|
||||
.unwrap()
|
||||
.contains("[0]"));
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(2))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(2))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert!(result["from failure module"]["error"]
|
||||
.as_object()
|
||||
@@ -2505,12 +2541,13 @@ async fn test_failure_module(db: Pool<Postgres>) {
|
||||
.unwrap()
|
||||
.contains("[0,1]"));
|
||||
|
||||
let result = RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(3))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
let result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.arg("n", json!(3))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
assert_eq!(json!({ "l": [0, 1, 2] }), result);
|
||||
}
|
||||
|
||||
@@ -2819,11 +2856,10 @@ async fn test_complex_flow_restart(db: Pool<Postgres>) {
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let first_run_result = RunJob::from(JobPayload::RawFlow {
|
||||
value: flow.clone(),
|
||||
path: None,
|
||||
restarted_from: None
|
||||
}).run_until_complete(&db, port).await;
|
||||
let first_run_result =
|
||||
RunJob::from(JobPayload::RawFlow { value: flow.clone(), path: None, restarted_from: None })
|
||||
.run_until_complete(&db, port)
|
||||
.await;
|
||||
|
||||
let restarted_flow_result = RunJob::from(JobPayload::RawFlow {
|
||||
value: flow.clone(),
|
||||
@@ -2833,10 +2869,16 @@ async fn test_complex_flow_restart(db: Pool<Postgres>) {
|
||||
step_id: "h".to_owned(),
|
||||
branch_or_iteration_n: None,
|
||||
}),
|
||||
}).run_until_complete(&db, port).await;
|
||||
})
|
||||
.run_until_complete(&db, port)
|
||||
.await;
|
||||
|
||||
let first_run_result_int = serde_json::from_value::<i32>(first_run_result.json_result().unwrap()).expect("first_run_result was not an int");
|
||||
let restarted_flow_result_int = serde_json::from_value::<i32>(restarted_flow_result.json_result().unwrap()).expect("restarted_flow_result was not an int");
|
||||
let first_run_result_int =
|
||||
serde_json::from_value::<i32>(first_run_result.json_result().unwrap())
|
||||
.expect("first_run_result was not an int");
|
||||
let restarted_flow_result_int =
|
||||
serde_json::from_value::<i32>(restarted_flow_result.json_result().unwrap())
|
||||
.expect("restarted_flow_result was not an int");
|
||||
assert_eq!(first_run_result_int, restarted_flow_result_int);
|
||||
}
|
||||
|
||||
@@ -2855,7 +2897,6 @@ async fn test_rust_client(db: Pool<Postgres>) {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
#[sqlx::test(fixtures("base", "schedule"))]
|
||||
async fn test_script_schedule_handlers(db: Pool<Postgres>) {
|
||||
@@ -2897,6 +2938,7 @@ async fn test_script_schedule_handlers(db: Pool<Postgres>) {
|
||||
no_flow_overlap: None,
|
||||
summary: None,
|
||||
tag: None,
|
||||
paused_until: None,
|
||||
};
|
||||
|
||||
let _ = client.create_schedule("test-workspace", &schedule).await;
|
||||
@@ -2961,6 +3003,7 @@ async fn test_script_schedule_handlers(db: Pool<Postgres>) {
|
||||
summary: None,
|
||||
no_flow_overlap: None,
|
||||
tag: None,
|
||||
paused_until: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -2980,7 +3023,7 @@ async fn test_script_schedule_handlers(db: Pool<Postgres>) {
|
||||
}
|
||||
|
||||
let uuid = uuid.unwrap().unwrap();
|
||||
|
||||
|
||||
let completed_job =
|
||||
query!("SELECT script_path FROM completed_job WHERE id = $1", uuid)
|
||||
.fetch_one(&db2)
|
||||
@@ -2999,7 +3042,6 @@ async fn test_script_schedule_handlers(db: Pool<Postgres>) {
|
||||
.await;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
#[sqlx::test(fixtures("base", "schedule"))]
|
||||
async fn test_flow_schedule_handlers(db: Pool<Postgres>) {
|
||||
@@ -3041,6 +3083,7 @@ async fn test_flow_schedule_handlers(db: Pool<Postgres>) {
|
||||
no_flow_overlap: None,
|
||||
summary: None,
|
||||
tag: None,
|
||||
paused_until: None,
|
||||
};
|
||||
|
||||
let _ = client.create_schedule("test-workspace", &schedule).await;
|
||||
@@ -3106,6 +3149,7 @@ async fn test_flow_schedule_handlers(db: Pool<Postgres>) {
|
||||
summary: None,
|
||||
no_flow_overlap: None,
|
||||
tag: None,
|
||||
paused_until: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -3126,7 +3170,7 @@ async fn test_flow_schedule_handlers(db: Pool<Postgres>) {
|
||||
}
|
||||
|
||||
let uuid = uuid.unwrap().unwrap();
|
||||
|
||||
|
||||
let completed_job =
|
||||
query!("SELECT script_path FROM completed_job WHERE id = $1", uuid)
|
||||
.fetch_one(&db2)
|
||||
@@ -3145,8 +3189,11 @@ async fn test_flow_schedule_handlers(db: Pool<Postgres>) {
|
||||
.await;
|
||||
}
|
||||
|
||||
|
||||
async fn run_deployed_relative_imports(db: &Pool<Postgres>, script_content: String, language: ScriptLang) {
|
||||
async fn run_deployed_relative_imports(
|
||||
db: &Pool<Postgres>,
|
||||
script_content: String,
|
||||
language: ScriptLang,
|
||||
) {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
@@ -3155,109 +3202,138 @@ async fn run_deployed_relative_imports(db: &Pool<Postgres>, script_content: Stri
|
||||
"SECRET_TOKEN".to_string(),
|
||||
);
|
||||
|
||||
client.create_script(
|
||||
"test-workspace",
|
||||
&NewScript {
|
||||
language: NewScriptLanguage::from_str(language.as_str()).unwrap(),
|
||||
content: script_content,
|
||||
path: "f/system/test_import".to_string(),
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
description: "".to_string(),
|
||||
draft_only: None,
|
||||
envs: vec![],
|
||||
is_template: None,
|
||||
kind: None,
|
||||
parent_hash: None,
|
||||
lock: None,
|
||||
summary: "".to_string(),
|
||||
tag: None,
|
||||
schema: std::collections::HashMap::new(),
|
||||
ws_error_handler_muted: Some(false),
|
||||
priority: None,
|
||||
delete_after_use: None,
|
||||
timeout: None,
|
||||
restart_unless_cancelled: None,
|
||||
deployment_message: None,
|
||||
concurrency_key: None,
|
||||
visible_to_runner_only: None,
|
||||
no_main_func: None,
|
||||
codebase: None
|
||||
},
|
||||
).await.unwrap();
|
||||
client
|
||||
.create_script(
|
||||
"test-workspace",
|
||||
&NewScript {
|
||||
language: NewScriptLanguage::from_str(language.as_str()).unwrap(),
|
||||
content: script_content,
|
||||
path: "f/system/test_import".to_string(),
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
description: "".to_string(),
|
||||
draft_only: None,
|
||||
envs: vec![],
|
||||
is_template: None,
|
||||
kind: None,
|
||||
parent_hash: None,
|
||||
lock: None,
|
||||
summary: "".to_string(),
|
||||
tag: None,
|
||||
schema: std::collections::HashMap::new(),
|
||||
ws_error_handler_muted: Some(false),
|
||||
priority: None,
|
||||
delete_after_use: None,
|
||||
timeout: None,
|
||||
restart_unless_cancelled: None,
|
||||
deployment_message: None,
|
||||
concurrency_key: None,
|
||||
visible_to_runner_only: None,
|
||||
no_main_func: None,
|
||||
codebase: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
let db2 = db.clone();
|
||||
in_test_worker(&db, async move {
|
||||
completed.next().await; // deployed script
|
||||
in_test_worker(
|
||||
&db,
|
||||
async move {
|
||||
completed.next().await; // deployed script
|
||||
|
||||
let script =
|
||||
query!("SELECT hash FROM script WHERE path = $1", "f/system/test_import".to_string())
|
||||
.fetch_one(&db2)
|
||||
.await
|
||||
.unwrap();
|
||||
let script = query!(
|
||||
"SELECT hash FROM script WHERE path = $1",
|
||||
"f/system/test_import".to_string()
|
||||
)
|
||||
.fetch_one(&db2)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let job = RunJob::from(JobPayload::ScriptHash {
|
||||
path: "f/system/test_import".to_string(),
|
||||
hash: ScriptHash(script.hash),
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
language,
|
||||
priority: None,
|
||||
}).push(&db2).await;
|
||||
let job = RunJob::from(JobPayload::ScriptHash {
|
||||
path: "f/system/test_import".to_string(),
|
||||
hash: ScriptHash(script.hash),
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
language,
|
||||
priority: None,
|
||||
})
|
||||
.push(&db2)
|
||||
.await;
|
||||
|
||||
completed.next().await; // completed job
|
||||
|
||||
completed.next().await; // completed job
|
||||
let result = completed_job(job, &db2).await.json_result().unwrap();
|
||||
|
||||
|
||||
let result = completed_job(job, &db2).await.json_result().unwrap();
|
||||
|
||||
|
||||
assert_eq!(result, serde_json::json!(["f/system/same_folder_script", "f/system/same_folder_script", "f/system_relative/different_folder_script", "f/system_relative/different_folder_script"]));
|
||||
|
||||
}, port).await;
|
||||
assert_eq!(
|
||||
result,
|
||||
serde_json::json!([
|
||||
"f/system/same_folder_script",
|
||||
"f/system/same_folder_script",
|
||||
"f/system_relative/different_folder_script",
|
||||
"f/system_relative/different_folder_script"
|
||||
])
|
||||
);
|
||||
},
|
||||
port,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
|
||||
async fn run_preview_relative_imports(db: &Pool<Postgres>, script_content: String, language: ScriptLang) {
|
||||
async fn run_preview_relative_imports(
|
||||
db: &Pool<Postgres>,
|
||||
script_content: String,
|
||||
language: ScriptLang,
|
||||
) {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
let db2 = db.clone();
|
||||
in_test_worker(&db, async move {
|
||||
let job = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content: script_content,
|
||||
path: Some("f/system/test_import".to_string()),
|
||||
language,
|
||||
lock: None,
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None
|
||||
})).push(&db2).await;
|
||||
in_test_worker(
|
||||
&db,
|
||||
async move {
|
||||
let job = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content: script_content,
|
||||
path: Some("f/system/test_import".to_string()),
|
||||
language,
|
||||
lock: None,
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
}))
|
||||
.push(&db2)
|
||||
.await;
|
||||
|
||||
completed.next().await; // completed job
|
||||
|
||||
completed.next().await; // completed job
|
||||
let result = completed_job(job, &db2).await.json_result().unwrap();
|
||||
|
||||
|
||||
let result = completed_job(job, &db2).await.json_result().unwrap();
|
||||
|
||||
|
||||
assert_eq!(result, serde_json::json!(["f/system/same_folder_script", "f/system/same_folder_script", "f/system_relative/different_folder_script", "f/system_relative/different_folder_script"]));
|
||||
|
||||
}, port).await;
|
||||
assert_eq!(
|
||||
result,
|
||||
serde_json::json!([
|
||||
"f/system/same_folder_script",
|
||||
"f/system/same_folder_script",
|
||||
"f/system_relative/different_folder_script",
|
||||
"f/system_relative/different_folder_script"
|
||||
])
|
||||
);
|
||||
},
|
||||
port,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_bun"))]
|
||||
async fn test_relative_imports_bun(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
@@ -3269,29 +3345,28 @@ import { main as test4 } from "../system_relative/different_folder_script.ts";
|
||||
export async function main() {
|
||||
return [test1(), test2(), test3(), test4()];
|
||||
}
|
||||
"#.to_string();
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Bun).await;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Bun).await;
|
||||
}
|
||||
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_bun"))]
|
||||
async fn test_nested_imports_bun(db: Pool<Postgres>) {
|
||||
|
||||
let content = r#"
|
||||
import { main as test } from "/f/system_relative/nested_script.ts";
|
||||
|
||||
export async function main() {
|
||||
return test();
|
||||
}
|
||||
"#.to_string();
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Bun).await;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Bun).await;
|
||||
}
|
||||
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_deno"))]
|
||||
async fn test_relative_imports_deno(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
@@ -3303,30 +3378,28 @@ import { main as test4 } from "../system_relative/different_folder_script.ts";
|
||||
export async function main() {
|
||||
return [test1(), test2(), test3(), test4()];
|
||||
}
|
||||
"#.to_string();
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Deno).await;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Deno).await;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_deno"))]
|
||||
async fn test_nested_imports_deno(db: Pool<Postgres>) {
|
||||
|
||||
let content = r#"
|
||||
import { main as test } from "/f/system_relative/nested_script.ts";
|
||||
|
||||
export async function main() {
|
||||
return test();
|
||||
}
|
||||
"#.to_string();
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Deno).await;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Deno).await;
|
||||
}
|
||||
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_python"))]
|
||||
async fn test_relative_imports_python(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
@@ -3337,24 +3410,23 @@ from ..system_relative.different_folder_script import main as test4
|
||||
|
||||
def main():
|
||||
return [test1(), test2(), test3(), test4()]
|
||||
"#.to_string();
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Python3).await;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Python3).await;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_python"))]
|
||||
async fn test_nested_imports_python(db: Pool<Postgres>) {
|
||||
|
||||
let content = r#"
|
||||
|
||||
from f.system_relative.nested_script import main as test
|
||||
|
||||
def main():
|
||||
return test()
|
||||
"#.to_string();
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Python3).await;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Python3).await;
|
||||
|
||||
@@ -86,6 +86,7 @@ datafusion = { workspace = true, optional = true}
|
||||
object_store = { workspace = true, optional = true}
|
||||
openidconnect = { workspace = true, optional = true}
|
||||
url = { workspace = true, optional = true}
|
||||
jsonwebtoken = { workspace = true }
|
||||
|
||||
pin-project.workspace = true
|
||||
crc.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.344.2
|
||||
version: 1.355.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -2373,6 +2373,10 @@ paths:
|
||||
- variable
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: variable list
|
||||
@@ -2854,6 +2858,10 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: resource list
|
||||
@@ -3494,17 +3502,24 @@ paths:
|
||||
- name: show_archived
|
||||
description: |
|
||||
(default false)
|
||||
show also the archived files.
|
||||
show only the archived files.
|
||||
when multiple archived hash share the same path, only the ones with the latest create_at
|
||||
are
|
||||
ed.
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: hide_without_main
|
||||
- name: include_without_main
|
||||
description: |
|
||||
(default false)
|
||||
hide the scripts without an exported main function
|
||||
include scripts without an exported main function
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: include_draft_only
|
||||
description: |
|
||||
(default false)
|
||||
include scripts that have no deployed version
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -4253,7 +4268,7 @@ paths:
|
||||
- name: show_archived
|
||||
description: |
|
||||
(default false)
|
||||
show also the archived files.
|
||||
show only the archived files.
|
||||
when multiple archived hash share the same path, only the ones with the latest create_at
|
||||
are displayed.
|
||||
in: query
|
||||
@@ -4266,6 +4281,13 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: include_draft_only
|
||||
description: |
|
||||
(default false)
|
||||
include items that have no deployed version
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: All flow
|
||||
@@ -4619,6 +4641,13 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: include_draft_only
|
||||
description: |
|
||||
(default false)
|
||||
include items that have no deployed version
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: All apps
|
||||
@@ -6440,6 +6469,10 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: schedule list
|
||||
@@ -6917,6 +6950,8 @@ paths:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
summary:
|
||||
type: string
|
||||
owners:
|
||||
type: array
|
||||
items:
|
||||
@@ -6951,6 +6986,8 @@ paths:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
summary:
|
||||
type: string
|
||||
owners:
|
||||
type: array
|
||||
items:
|
||||
@@ -9842,6 +9879,11 @@ components:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- path
|
||||
- resource_type
|
||||
@@ -9875,6 +9917,11 @@ components:
|
||||
type: boolean
|
||||
account:
|
||||
type: number
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- path
|
||||
- resource_type
|
||||
@@ -9892,6 +9939,11 @@ components:
|
||||
schema: {}
|
||||
description:
|
||||
type: string
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- name
|
||||
|
||||
@@ -9957,6 +10009,9 @@ components:
|
||||
type: boolean
|
||||
tag:
|
||||
type: string
|
||||
paused_until:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- path
|
||||
- edited_by
|
||||
@@ -10032,6 +10087,9 @@ components:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
paused_until:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- path
|
||||
- schedule
|
||||
@@ -10074,6 +10132,9 @@ components:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
paused_until:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- schedule
|
||||
- timezone
|
||||
@@ -10126,6 +10187,13 @@ components:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
summary:
|
||||
type: string
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- name
|
||||
- owners
|
||||
|
||||
@@ -279,6 +279,18 @@ async fn list_apps(
|
||||
sqlb.and_where_is_not_null("favorite.path");
|
||||
}
|
||||
|
||||
if let Some(path_start) = &lq.path_start {
|
||||
sqlb.and_where_like_left("app.path", path_start);
|
||||
}
|
||||
|
||||
if let Some(path_exact) = &lq.path_exact {
|
||||
sqlb.and_where_eq("app.path", "?".bind(path_exact));
|
||||
}
|
||||
|
||||
if !lq.include_draft_only.unwrap_or(false) || authed.is_operator {
|
||||
sqlb.and_where("app.draft_only IS NOT TRUE");
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let rows = sqlx::query_as::<_, ListableApp>(&sql)
|
||||
@@ -648,6 +660,7 @@ async fn create_app(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
@@ -938,6 +951,7 @@ async fn update_app(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
@@ -1150,6 +1164,7 @@ async fn execute_component(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
@@ -4,3 +4,13 @@ pub async fn validate_license_key(_license_key: String) -> anyhow::Result<String
|
||||
// Implementation is not open source
|
||||
Err(anyhow!("License can't be validated in Windmill CE"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn jwt_ext_auth(
|
||||
_w_id: Option<&String>,
|
||||
_token: &str,
|
||||
) -> Option<(crate::db::ApiAuthed, usize)> {
|
||||
// Implementation is not open source
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -143,10 +143,10 @@ async fn list_flows(
|
||||
sqlb.and_where_eq("archived", lq.show_archived.unwrap_or(false));
|
||||
|
||||
if let Some(ps) = &lq.path_start {
|
||||
sqlb.and_where_like_left("path", "?".bind(ps));
|
||||
sqlb.and_where_like_left("o.path", ps);
|
||||
}
|
||||
if let Some(p) = &lq.path_exact {
|
||||
sqlb.and_where_eq("path", "?".bind(p));
|
||||
sqlb.and_where_eq("o.path", "?".bind(p));
|
||||
}
|
||||
if let Some(cb) = &lq.edited_by {
|
||||
sqlb.and_where_eq("edited_by", "?".bind(cb));
|
||||
@@ -155,6 +155,10 @@ async fn list_flows(
|
||||
sqlb.and_where_is_not_null("favorite.path");
|
||||
}
|
||||
|
||||
if !lq.include_draft_only.unwrap_or(false) || authed.is_operator {
|
||||
sqlb.and_where("o.draft_only IS NOT TRUE");
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let rows = sqlx::query_as::<_, ListableFlow>(&sql)
|
||||
@@ -393,6 +397,7 @@ async fn create_flow(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -539,7 +544,7 @@ async fn update_flow(
|
||||
clear_schedule(tx.transaction_mut(), &schedule.path, &w_id).await?;
|
||||
|
||||
if schedule.enabled {
|
||||
tx = push_scheduled_job(&db, tx, &schedule).await?;
|
||||
tx = push_scheduled_job(&db, tx, &schedule, None).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,6 +614,7 @@ async fn update_flow(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
|
||||
@@ -56,11 +56,15 @@ pub struct Folder {
|
||||
pub display_name: String,
|
||||
pub owners: Vec<String>,
|
||||
pub extra_perms: serde_json::Value,
|
||||
pub summary: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NewFolder {
|
||||
pub name: String,
|
||||
pub summary: Option<String>,
|
||||
pub display_name: Option<String>,
|
||||
pub owners: Option<Vec<String>>,
|
||||
pub extra_perms: Option<serde_json::Value>,
|
||||
@@ -68,6 +72,7 @@ pub struct NewFolder {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UpdateFolder {
|
||||
pub summary: Option<String>,
|
||||
pub display_name: Option<String>,
|
||||
pub owners: Option<Vec<String>>,
|
||||
pub extra_perms: Option<serde_json::Value>,
|
||||
@@ -90,7 +95,7 @@ async fn list_folders(
|
||||
|
||||
let rows = sqlx::query_as!(
|
||||
Folder,
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
w_id,
|
||||
per_page as i64,
|
||||
offset as i64
|
||||
@@ -199,12 +204,14 @@ async fn create_folder(
|
||||
|
||||
sqlx::query_as!(
|
||||
Folder,
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, $2, $3, $4, $5)",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, now())",
|
||||
w_id,
|
||||
ng.name,
|
||||
ng.display_name.unwrap_or(ng.name.clone()),
|
||||
&owners,
|
||||
extra_perms,
|
||||
ng.summary,
|
||||
authed.username
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
@@ -283,6 +290,12 @@ async fn update_folder(
|
||||
sqlb.set("display_name", "?".bind(&display_name));
|
||||
}
|
||||
|
||||
if let Some(summary) = ng.summary {
|
||||
sqlb.set("summary", "?".bind(&summary));
|
||||
}
|
||||
|
||||
sqlb.set("edited_at", "now()");
|
||||
|
||||
if !authed.is_admin {
|
||||
let prefixed_username = format!("u/{}", authed.username);
|
||||
if ng.owners.as_ref().is_some_and(|x| {
|
||||
@@ -380,7 +393,7 @@ pub async fn get_folderopt<'c>(
|
||||
) -> Result<Option<Folder>> {
|
||||
let folderopt = sqlx::query_as!(
|
||||
Folder,
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
name,
|
||||
w_id
|
||||
)
|
||||
@@ -603,32 +616,6 @@ async fn add_owner(
|
||||
Ok(format!("Added {} to folder {}", owner, name))
|
||||
}
|
||||
|
||||
pub async fn get_folders_for_user(
|
||||
w_id: &str,
|
||||
username: &str,
|
||||
groups: &[String],
|
||||
db: &DB,
|
||||
) -> Result<Vec<(String, bool, bool)>> {
|
||||
let mut perms = groups
|
||||
.into_iter()
|
||||
.map(|x| format!("g/{}", x))
|
||||
.collect::<Vec<_>>();
|
||||
perms.insert(0, format!("u/{}", username));
|
||||
let folders = sqlx::query!(
|
||||
"SELECT name, (EXISTS (SELECT 1 FROM (SELECT key, value FROM jsonb_each_text(extra_perms) WHERE key = ANY($1)) t WHERE value::boolean IS true)) as write, $1 && owners::text[] as owner FROM folder
|
||||
WHERE extra_perms ?| $1 AND workspace_id = $2",
|
||||
&perms[..],
|
||||
w_id,
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| (x.name, x.write.unwrap_or(false), x.owner.unwrap_or(false)))
|
||||
.collect();
|
||||
|
||||
Ok(folders)
|
||||
}
|
||||
|
||||
async fn remove_owner(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::{db::DB, users::get_groups_for_user, utils::require_super_admin};
|
||||
use crate::{db::DB, utils::require_super_admin};
|
||||
|
||||
use axum::{
|
||||
extract::{Extension, Path, Query},
|
||||
@@ -17,11 +17,12 @@ use axum::{
|
||||
use windmill_audit::audit_ee::audit_log;
|
||||
use windmill_audit::ActionKind;
|
||||
use windmill_common::worker::CLOUD_HOSTED;
|
||||
use windmill_common::{db::UserDB, users::username_to_permissioned_as};
|
||||
use windmill_common::{
|
||||
auth::get_groups_for_user,
|
||||
error::{Error, JsonResult, Result},
|
||||
utils::{not_found_if_none, paginate, Pagination},
|
||||
};
|
||||
use windmill_common::{db::UserDB, users::username_to_permissioned_as};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{query_scalar, FromRow, Postgres, Transaction};
|
||||
|
||||
@@ -28,8 +28,8 @@ use windmill_common::worker::TMP_DIR;
|
||||
use windmill_common::scripts::PREVIEW_IS_CODEBASE_HASH;
|
||||
use windmill_common::variables::get_workspace_key;
|
||||
|
||||
use crate::concurrency_groups::join_concurrency_key;
|
||||
use crate::add_webhook_allowed_origin;
|
||||
use crate::concurrency_groups::join_concurrency_key;
|
||||
use crate::db::ApiAuthed;
|
||||
|
||||
use crate::{
|
||||
@@ -346,18 +346,26 @@ async fn cancel_job_api(
|
||||
},
|
||||
};
|
||||
|
||||
let (mut tx, job_option) = windmill_queue::cancel_job(
|
||||
&audit_author.username,
|
||||
reason,
|
||||
id,
|
||||
&w_id,
|
||||
tx,
|
||||
&db,
|
||||
rsmq,
|
||||
false,
|
||||
opt_authed.is_none(),
|
||||
let (mut tx, job_option) = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(120),
|
||||
windmill_queue::cancel_job(
|
||||
&audit_author.username,
|
||||
reason,
|
||||
id,
|
||||
&w_id,
|
||||
tx,
|
||||
&db,
|
||||
rsmq,
|
||||
false,
|
||||
opt_authed.is_none(),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!(
|
||||
"timeout after 120s while cancelling job {id} in {w_id}: {e:#}"
|
||||
))
|
||||
})??;
|
||||
|
||||
if let Some(id) = job_option {
|
||||
audit_log(
|
||||
@@ -453,18 +461,26 @@ async fn force_cancel(
|
||||
},
|
||||
};
|
||||
|
||||
let (mut tx, job_option) = windmill_queue::cancel_job(
|
||||
&audit_author.username,
|
||||
reason,
|
||||
id,
|
||||
&w_id,
|
||||
tx,
|
||||
&db,
|
||||
rsmq,
|
||||
true,
|
||||
opt_authed.is_none(),
|
||||
let (mut tx, job_option) = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(120),
|
||||
windmill_queue::cancel_job(
|
||||
&audit_author.username,
|
||||
reason,
|
||||
id,
|
||||
&w_id,
|
||||
tx,
|
||||
&db,
|
||||
rsmq,
|
||||
true,
|
||||
opt_authed.is_none(),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!(
|
||||
"timeout after 120s while cancelling job {id} in {w_id}: {e:#}"
|
||||
))
|
||||
})??;
|
||||
|
||||
if let Some(id) = job_option {
|
||||
audit_log(
|
||||
@@ -1327,9 +1343,7 @@ async fn list_filtered_uuids(
|
||||
) -> error::JsonResult<Vec<Uuid>> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
let mut sqlb = SqlBuilder::select_from("queue")
|
||||
.fields(&["id"])
|
||||
.clone();
|
||||
let mut sqlb = SqlBuilder::select_from("queue").fields(&["id"]).clone();
|
||||
|
||||
sqlb = join_concurrency_key(lq.concurrency_key.as_ref(), sqlb);
|
||||
|
||||
@@ -2624,6 +2638,7 @@ pub async fn run_flow_by_path(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -2713,6 +2728,7 @@ pub async fn restart_flow(
|
||||
None,
|
||||
None,
|
||||
completed_job.priority,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -2765,6 +2781,7 @@ pub async fn run_script_by_path(
|
||||
timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -2841,6 +2858,7 @@ pub async fn run_workflow_as_code(
|
||||
timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
@@ -3089,10 +3107,9 @@ pub async fn run_wait_result_job_by_path_get(
|
||||
if method == http::Method::HEAD {
|
||||
return Ok(Json(serde_json::json!("")).into_response());
|
||||
}
|
||||
let payload_r = run_query
|
||||
.payload
|
||||
.map(decode_payload)
|
||||
.map(|x| x.map_err(|e| Error::InternalErr(e.to_string())));
|
||||
let payload_r = run_query.payload.map(decode_payload).map(|x| {
|
||||
x.map_err(|e| Error::InternalErr(format!("Impossible to decode query payload: {e:#?}")))
|
||||
});
|
||||
|
||||
let mut payload_args = if let Some(payload) = payload_r {
|
||||
payload?
|
||||
@@ -3139,6 +3156,7 @@ pub async fn run_wait_result_job_by_path_get(
|
||||
timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -3166,11 +3184,11 @@ pub async fn run_wait_result_flow_by_path_get(
|
||||
if method == http::Method::HEAD {
|
||||
return Ok(Json(serde_json::json!("")).into_response());
|
||||
}
|
||||
let payload_r = run_query
|
||||
.payload
|
||||
.clone()
|
||||
.map(decode_payload)
|
||||
.map(|x| x.map_err(|e| Error::InternalErr(e.to_string())));
|
||||
let payload_r = run_query.payload.clone().map(decode_payload).map(|x| {
|
||||
x.map_err(|e| {
|
||||
error::Error::InternalErr(format!("Impossible to decode query payload: {e:#?}"))
|
||||
})
|
||||
});
|
||||
|
||||
let mut payload_args = if let Some(payload) = payload_r {
|
||||
payload?
|
||||
@@ -3258,6 +3276,7 @@ async fn run_wait_result_script_by_path_internal(
|
||||
timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -3338,6 +3357,7 @@ pub async fn run_wait_result_script_by_hash(
|
||||
timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -3420,6 +3440,7 @@ async fn run_wait_result_flow_by_path_internal(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -3487,6 +3508,7 @@ async fn run_preview_script(
|
||||
run_query.timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -3568,6 +3590,7 @@ async fn run_bundle_preview_script(
|
||||
run_query.timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
job_id = Some(uuid);
|
||||
@@ -3592,7 +3615,7 @@ async fn run_bundle_preview_script(
|
||||
{
|
||||
let path = windmill_common::s3_helpers::bundle(&w_id, &id);
|
||||
if let Err(e) = os
|
||||
.put(&object_store::path::Path::from(path.clone()), data)
|
||||
.put(&object_store::path::Path::from(path.clone()), data.into())
|
||||
.await
|
||||
{
|
||||
tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e);
|
||||
@@ -3706,6 +3729,7 @@ async fn run_dependencies_job(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -3743,7 +3767,7 @@ async fn run_flow_dependencies_job(
|
||||
PushIsolationLevel::IsolatedRoot(db.clone(), rsmq),
|
||||
&w_id,
|
||||
JobPayload::RawFlowDependencies { path: req.path, flow_value: req.flow_value },
|
||||
PushArgs::empty(),
|
||||
HashMap::from([("skip_flow_update".to_string(), to_raw_value(&true))]).into(),
|
||||
authed.display_username(),
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
@@ -3760,6 +3784,7 @@ async fn run_flow_dependencies_job(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -3866,6 +3891,7 @@ async fn add_batch_jobs(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx = PushIsolationLevel::Transaction(ntx);
|
||||
@@ -3935,13 +3961,16 @@ async fn add_batch_jobs(
|
||||
)
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"INSERT INTO concurrency_key (job_id, key) SELECT id, $1 FROM unnest($2::uuid[]) as id",
|
||||
custom_concurrency_key,
|
||||
&uuids
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
if let Some(custom_concurrency_key) = custom_concurrency_key {
|
||||
sqlx::query!(
|
||||
"INSERT INTO concurrency_key (job_id, key) SELECT id, $1 FROM unnest($2::uuid[]) as id",
|
||||
custom_concurrency_key,
|
||||
&uuids
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(Json(uuids))
|
||||
}
|
||||
@@ -3992,6 +4021,7 @@ async fn run_preview_flow_job(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -4068,6 +4098,7 @@ pub async fn run_job_by_hash(
|
||||
timeout,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
@@ -12,6 +12,7 @@ use axum::{routing::get, Json, Router};
|
||||
use hmac::Mac;
|
||||
use hyper::HeaderMap;
|
||||
|
||||
use itertools::Itertools;
|
||||
use oauth2::{Client as OClient, *};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Postgres, Transaction};
|
||||
@@ -49,8 +50,8 @@ pub enum InstanceEvent {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientWithScopes {
|
||||
_client: OClient,
|
||||
scopes: Vec<String>,
|
||||
extra_params: Option<HashMap<String, String>>,
|
||||
_scopes: Vec<String>,
|
||||
_extra_params: Option<HashMap<String, String>>,
|
||||
_extra_params_callback: Option<HashMap<String, String>>,
|
||||
_allowed_domains: Option<Vec<String>>,
|
||||
_userinfo_url: Option<String>,
|
||||
@@ -120,25 +121,12 @@ async fn list_logins() -> error::JsonResult<Logins> {
|
||||
return Ok(Json(Logins { oauth: vec![], saml: None }));
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ScopesAndParams {
|
||||
scopes: Vec<String>,
|
||||
extra_params: Option<HashMap<String, String>>,
|
||||
}
|
||||
async fn list_connects() -> error::JsonResult<HashMap<String, ScopesAndParams>> {
|
||||
async fn list_connects() -> error::JsonResult<Vec<String>> {
|
||||
Ok(Json(
|
||||
(&OAUTH_CLIENTS.read().await.connects)
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k.to_owned(),
|
||||
ScopesAndParams {
|
||||
scopes: v.scopes.clone(),
|
||||
extra_params: v.extra_params.clone(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<HashMap<String, ScopesAndParams>>(),
|
||||
.keys()
|
||||
.map(|x| x.to_owned())
|
||||
.collect_vec(),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,14 @@ async fn list_apps(
|
||||
sqlb.and_where_is_not_null("favorite.path");
|
||||
}
|
||||
|
||||
if let Some(path_start) = &lq.path_start {
|
||||
sqlb.and_where_like_left("app.path", path_start);
|
||||
}
|
||||
|
||||
if let Some(path_exact) = &lq.path_exact {
|
||||
sqlb.and_where_eq("app.path", "?".bind(path_exact));
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let rows = sqlx::query_as::<_, ListableApp>(&sql)
|
||||
|
||||
@@ -69,6 +69,8 @@ pub struct ResourceType {
|
||||
pub name: String,
|
||||
pub schema: Option<serde_json::Value>,
|
||||
pub description: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -92,6 +94,8 @@ pub struct Resource {
|
||||
pub description: Option<String>,
|
||||
pub resource_type: String,
|
||||
pub extra_perms: serde_json::Value,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize, Deserialize)]
|
||||
@@ -102,6 +106,8 @@ pub struct ListableResource {
|
||||
pub description: Option<String>,
|
||||
pub resource_type: String,
|
||||
pub extra_perms: serde_json::Value,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub is_linked: Option<bool>,
|
||||
pub is_refreshed: Option<bool>,
|
||||
pub is_oauth: Option<bool>,
|
||||
@@ -128,6 +134,7 @@ struct EditResource {
|
||||
pub struct ListResourceQuery {
|
||||
resource_type: Option<String>,
|
||||
resource_type_exclude: Option<String>,
|
||||
path_start: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, FromRow)]
|
||||
@@ -209,6 +216,8 @@ async fn list_resources(
|
||||
"variable.is_oauth",
|
||||
"variable.account",
|
||||
"account.refresh_error",
|
||||
"resource.created_by",
|
||||
"resource.edited_at",
|
||||
])
|
||||
.left()
|
||||
.join("variable")
|
||||
@@ -241,6 +250,10 @@ async fn list_resources(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path_start) = &lq.path_start {
|
||||
sqlb.and_where_like_left("resource.path", path_start);
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let rows = sqlx::query_as::<_, ListableResource>(&sql)
|
||||
@@ -642,14 +655,15 @@ async fn create_resource(
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO resource
|
||||
(workspace_id, path, value, description, resource_type)
|
||||
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path)
|
||||
DO UPDATE SET value = $3, description = $4, resource_type = $5",
|
||||
(workspace_id, path, value, description, resource_type, created_by, edited_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)
|
||||
DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
|
||||
w_id,
|
||||
resource.path,
|
||||
raw_json as sqlx::types::Json<&RawValue>,
|
||||
resource.description,
|
||||
resource.resource_type,
|
||||
authed.username
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
@@ -772,6 +786,8 @@ async fn update_resource(
|
||||
sqlb.set_str("description", ndesc);
|
||||
}
|
||||
|
||||
sqlb.set_str("edited_at", "now()");
|
||||
|
||||
sqlb.returning("path");
|
||||
let authed = maybe_refresh_folders(path, &w_id, authed, &db).await;
|
||||
|
||||
@@ -853,7 +869,7 @@ async fn update_resource_value(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE resource SET value = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
"UPDATE resource SET value = $1, edited_at = now() WHERE path = $2 AND workspace_id = $3",
|
||||
nv.value,
|
||||
path,
|
||||
w_id
|
||||
@@ -979,12 +995,13 @@ async fn create_resource_type(
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO resource_type
|
||||
(workspace_id, name, schema, description)
|
||||
VALUES ($1, $2, $3, $4)",
|
||||
(workspace_id, name, schema, description, created_by, edited_at)
|
||||
VALUES ($1, $2, $3, $4, $5, now())",
|
||||
w_id,
|
||||
resource_type.name,
|
||||
resource_type.schema,
|
||||
resource_type.description,
|
||||
authed.username
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
@@ -1120,6 +1137,7 @@ async fn update_resource_type(
|
||||
if let Some(ndesc) = ns.description {
|
||||
sqlb.set_str("description", ndesc);
|
||||
}
|
||||
sqlb.set_str("edited_at", "now()");
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ pub struct NewSchedule {
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
pub retry: Option<serde_json::Value>,
|
||||
pub tag: Option<String>,
|
||||
pub paused_until: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -157,9 +158,9 @@ async fn create_schedule(
|
||||
"INSERT INTO schedule (workspace_id, path, schedule, timezone, edited_by, script_path, \
|
||||
is_flow, args, enabled, email, on_failure, on_failure_times, on_failure_exact, \
|
||||
on_failure_extra_args, on_recovery, on_recovery_times, on_recovery_extra_args, \
|
||||
ws_error_handler_muted, retry, summary, no_flow_overlap, tag \
|
||||
ws_error_handler_muted, retry, summary, no_flow_overlap, tag, paused_until \
|
||||
) VALUES ( \
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22 \
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23 \
|
||||
) RETURNING *")
|
||||
.bind(&w_id)
|
||||
.bind(&ns.path)
|
||||
@@ -183,6 +184,7 @@ async fn create_schedule(
|
||||
.bind(&ns.summary)
|
||||
.bind(&ns.no_flow_overlap.unwrap_or(false))
|
||||
.bind(&ns.tag)
|
||||
.bind(&ns.paused_until)
|
||||
.fetch_one(&mut tx)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("inserting schedule in {w_id}: {e:#}")))?;
|
||||
@@ -219,7 +221,7 @@ async fn create_schedule(
|
||||
.await?;
|
||||
|
||||
if ns.enabled.unwrap_or(true) {
|
||||
tx = push_scheduled_job(&db, tx, &schedule).await?
|
||||
tx = push_scheduled_job(&db, tx, &schedule, Some(&authed.clone().into())).await?
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
@@ -247,8 +249,8 @@ async fn edit_schedule(
|
||||
"UPDATE schedule SET schedule = $1, timezone = $2, args = $3, on_failure = $4, on_failure_times = $5, \
|
||||
on_failure_exact = $6, on_failure_extra_args = $7, on_recovery = $8, on_recovery_times = $9, \
|
||||
on_recovery_extra_args = $10, ws_error_handler_muted = $11, retry = $12, summary = $13, \
|
||||
no_flow_overlap = $14, tag = $15
|
||||
WHERE path = $16 AND workspace_id = $17 RETURNING *")
|
||||
no_flow_overlap = $14, tag = $15, paused_until = $16
|
||||
WHERE path = $17 AND workspace_id = $18 RETURNING *")
|
||||
.bind(&es.schedule)
|
||||
.bind(&es.timezone)
|
||||
.bind(&es.args)
|
||||
@@ -264,6 +266,7 @@ async fn edit_schedule(
|
||||
.bind(&es.summary)
|
||||
.bind(&es.no_flow_overlap.unwrap_or(false))
|
||||
.bind(&es.tag)
|
||||
.bind(&es.paused_until)
|
||||
.bind(&path)
|
||||
.bind(&w_id)
|
||||
.fetch_one(&mut tx)
|
||||
@@ -299,7 +302,7 @@ async fn edit_schedule(
|
||||
.await?;
|
||||
|
||||
if schedule.enabled {
|
||||
tx = push_scheduled_job(&db, tx, &schedule).await?;
|
||||
tx = push_scheduled_job(&db, tx, &schedule, None).await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
@@ -313,6 +316,7 @@ pub struct ListScheduleQuery {
|
||||
pub path: Option<String>,
|
||||
pub is_flow: Option<bool>,
|
||||
pub args: Option<String>,
|
||||
pub path_start: Option<String>,
|
||||
}
|
||||
|
||||
async fn list_schedule(
|
||||
@@ -339,6 +343,9 @@ async fn list_schedule(
|
||||
if let Some(args) = &lsq.args {
|
||||
sqlb.and_where("args @> ?".bind(&args.replace("'", "''")));
|
||||
}
|
||||
if let Some(path_start) = &lsq.path_start {
|
||||
sqlb.and_where_like_left("path", path_start);
|
||||
}
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let rows = sqlx::query_as::<_, Schedule>(&sql)
|
||||
.fetch_all(&mut *tx)
|
||||
@@ -375,6 +382,7 @@ pub struct ScheduleWJobs {
|
||||
pub summary: Option<String>,
|
||||
pub no_flow_overlap: bool,
|
||||
pub tag: Option<String>,
|
||||
pub paused_until: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
async fn list_schedule_with_jobs(
|
||||
@@ -506,7 +514,7 @@ pub async fn set_enabled(
|
||||
.await?;
|
||||
|
||||
if payload.enabled {
|
||||
tx = push_scheduled_job(&db, tx, &schedule).await?;
|
||||
tx = push_scheduled_job(&db, tx, &schedule, None).await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
@@ -554,7 +562,7 @@ pub async fn set_enabled(
|
||||
// .await?;
|
||||
|
||||
// if payload.enabled {
|
||||
// tx = push_scheduled_job(&db, tx, &schedule).await?;
|
||||
// tx = push_scheduled_job(&db, tx, &schedule, None).await?;
|
||||
// }
|
||||
// tx.commit().await?;
|
||||
|
||||
@@ -786,6 +794,7 @@ pub struct EditSchedule {
|
||||
pub retry: Option<serde_json::Value>,
|
||||
pub no_flow_overlap: Option<bool>,
|
||||
pub tag: Option<String>,
|
||||
pub paused_until: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
pub async fn clear_schedule<'c>(
|
||||
|
||||
@@ -226,10 +226,14 @@ async fn list_scripts(
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
|
||||
if authed.is_operator || lq.hide_without_main.unwrap_or(false) {
|
||||
if !lq.include_without_main.unwrap_or(false) || authed.is_operator {
|
||||
sqlb.and_where("o.no_main_func IS NOT TRUE");
|
||||
}
|
||||
|
||||
if !lq.include_draft_only.unwrap_or(false) || authed.is_operator {
|
||||
sqlb.and_where("draft_only IS NOT TRUE");
|
||||
}
|
||||
|
||||
if lq.show_archived.unwrap_or(false) {
|
||||
sqlb.and_where_eq(
|
||||
"o.created_at",
|
||||
@@ -242,10 +246,10 @@ async fn list_scripts(
|
||||
sqlb.and_where_eq("archived", false);
|
||||
}
|
||||
if let Some(ps) = &lq.path_start {
|
||||
sqlb.and_where_like_left("path", "?".bind(ps));
|
||||
sqlb.and_where_like_left("o.path", ps);
|
||||
}
|
||||
if let Some(p) = &lq.path_exact {
|
||||
sqlb.and_where_eq("path", "?".bind(p));
|
||||
sqlb.and_where_eq("o.path", "?".bind(p));
|
||||
}
|
||||
if let Some(cb) = &lq.created_by {
|
||||
sqlb.and_where_eq("created_by", "?".bind(cb));
|
||||
@@ -374,7 +378,7 @@ async fn create_snapshot_script(
|
||||
{
|
||||
let path = windmill_common::s3_helpers::bundle(&w_id, &hash);
|
||||
if let Err(e) = os
|
||||
.put(&object_store::path::Path::from(path.clone()), data)
|
||||
.put(&object_store::path::Path::from(path.clone()), data.into())
|
||||
.await
|
||||
{
|
||||
tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e);
|
||||
@@ -648,7 +652,7 @@ async fn create_script_internal<'c>(
|
||||
clear_schedule(tx.transaction_mut(), &schedule.path, &w_id).await?;
|
||||
|
||||
if schedule.enabled {
|
||||
tx = push_scheduled_job(&db, tx, &schedule).await?;
|
||||
tx = push_scheduled_job(&db, tx, &schedule, None).await?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -751,6 +755,7 @@ async fn create_script_internal<'c>(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
Ok((hash, new_tx))
|
||||
|
||||
@@ -129,7 +129,7 @@ pub async fn test_s3_bucket(
|
||||
));
|
||||
tracing::info!("Testing blob storage at path: {path}");
|
||||
client
|
||||
.put(&path, Bytes::from_static(b"hello"))
|
||||
.put(&path, object_store::PutPayload::from_static(b"hello"))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("error writing file to {path}: {e:#}"))?;
|
||||
let content = client
|
||||
|
||||
@@ -38,10 +38,7 @@ const TWO_HUNDRED: &str = "200.html";
|
||||
|
||||
fn serve_path(path: &str) -> Response<Body> {
|
||||
if path.starts_with("api/") {
|
||||
return Response::builder()
|
||||
.status(404)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
return Response::builder().status(404).body(Body::empty()).unwrap();
|
||||
}
|
||||
match Asset::get(path) {
|
||||
Some(content) => {
|
||||
@@ -50,7 +47,7 @@ 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 {
|
||||
if mime.as_ref() == mime::APPLICATION_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");
|
||||
@@ -62,10 +59,9 @@ fn serve_path(path: &str) -> Response<Body> {
|
||||
|
||||
res.body(body).unwrap()
|
||||
}
|
||||
None if path.starts_with("_app/") => Response::builder()
|
||||
.status(404)
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
None if path.starts_with("_app/") => {
|
||||
Response::builder().status(404).body(Body::empty()).unwrap()
|
||||
}
|
||||
None => serve_path(TWO_HUNDRED),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::utils::{
|
||||
get_instance_username_or_create_pending,
|
||||
};
|
||||
use crate::{
|
||||
db::DB, folders::get_folders_for_user, utils::require_super_admin, webhook_util::WebhookShared,
|
||||
db::DB, utils::require_super_admin, webhook_util::WebhookShared,
|
||||
workspaces::invite_user_to_all_auto_invite_worspaces, BASE_URL, COOKIE_DOMAIN, IS_SECURE,
|
||||
};
|
||||
use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
|
||||
@@ -30,6 +30,7 @@ use axum::{
|
||||
routing::{delete, get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use chrono::TimeZone;
|
||||
use hyper::{header::LOCATION, StatusCode};
|
||||
use lazy_static::lazy_static;
|
||||
use quick_cache::sync::Cache;
|
||||
@@ -47,9 +48,10 @@ use windmill_common::users::truncate_token;
|
||||
use windmill_common::utils::{paginate, send_email};
|
||||
use windmill_common::worker::{CLOUD_HOSTED, SERVER_CONFIG};
|
||||
use windmill_common::{
|
||||
auth::{get_folders_for_user, get_groups_for_user, JWTAuthClaims, JWT_SECRET},
|
||||
db::UserDB,
|
||||
error::{self, Error, JsonResult, Result},
|
||||
users::SUPERADMIN_SECRET_EMAIL,
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
utils::{not_found_if_none, rd_string, require_admin, Pagination, StripPath},
|
||||
};
|
||||
use windmill_git_sync::handle_deployment_metadata;
|
||||
@@ -164,6 +166,78 @@ impl AuthCache {
|
||||
Some(ExpiringAuthCache { authed, expiry }) if expiry > chrono::Utc::now() => {
|
||||
Some(authed)
|
||||
}
|
||||
#[cfg(feature = "enterprise")]
|
||||
_ if token.starts_with("jwt_ext_") => {
|
||||
let authed_and_exp =
|
||||
crate::ee::jwt_ext_auth(w_id.as_ref(), token.trim_start_matches("jwt_ext_"))
|
||||
.await;
|
||||
|
||||
if let Some((authed, exp)) = authed_and_exp.clone() {
|
||||
self.cache.insert(
|
||||
key,
|
||||
ExpiringAuthCache {
|
||||
authed: authed.clone(),
|
||||
expiry: chrono::Utc.timestamp_nanos(exp as i64 * 1_000_000_000),
|
||||
},
|
||||
);
|
||||
|
||||
Some(authed)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ if token.starts_with("jwt_") => {
|
||||
let jwt_secret = JWT_SECRET.read().await;
|
||||
if !jwt_secret.is_empty() {
|
||||
let jwt_token = token.trim_start_matches("jwt_");
|
||||
|
||||
let jwt_result = jsonwebtoken::decode::<JWTAuthClaims>(
|
||||
jwt_token,
|
||||
&jsonwebtoken::DecodingKey::from_secret(jwt_secret.as_bytes()),
|
||||
&jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256),
|
||||
);
|
||||
|
||||
match jwt_result {
|
||||
Ok(payload) => {
|
||||
if w_id.is_some_and(|w_id| w_id != payload.claims.workspace_id) {
|
||||
tracing::error!("JWT auth error: workspace_id mismatch");
|
||||
return None;
|
||||
}
|
||||
|
||||
let username_override =
|
||||
username_override_from_label(payload.claims.label);
|
||||
let authed = crate::db::ApiAuthed {
|
||||
email: payload.claims.email,
|
||||
username: payload.claims.username,
|
||||
is_admin: payload.claims.is_admin,
|
||||
is_operator: payload.claims.is_operator,
|
||||
groups: payload.claims.groups,
|
||||
folders: payload.claims.folders,
|
||||
scopes: None,
|
||||
username_override,
|
||||
};
|
||||
|
||||
self.cache.insert(
|
||||
key,
|
||||
ExpiringAuthCache {
|
||||
authed: authed.clone(),
|
||||
expiry: chrono::Utc
|
||||
.timestamp_nanos(payload.claims.exp as i64 * 1_000_000_000),
|
||||
},
|
||||
);
|
||||
|
||||
Some(authed)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("JWT auth error: {:?}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tracing::error!("JWT auth error: no jwt secret set");
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
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() \
|
||||
@@ -513,7 +587,9 @@ where
|
||||
{
|
||||
if let Some(authed) = cache.get_authed(workspace_id.clone(), &token).await {
|
||||
parts.extensions.insert(authed.clone());
|
||||
if authed.scopes.is_some() && (path_vec.len() < 3 || path_vec[4] != "jobs")
|
||||
if authed.scopes.is_some()
|
||||
&& (path_vec.len() < 3
|
||||
|| (path_vec[4] != "jobs" && path_vec[4] != "jobs_u"))
|
||||
{
|
||||
return Err((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
@@ -1046,24 +1122,6 @@ async fn get_user(w_id: &str, username: &str, db: &DB) -> Result<Option<UserInfo
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn get_groups_for_user(
|
||||
w_id: &str,
|
||||
username: &str,
|
||||
email: &str,
|
||||
db: &DB,
|
||||
) -> Result<Vec<String>> {
|
||||
let groups = sqlx::query_scalar!(
|
||||
"SELECT group_ FROM usr_to_group where usr = $1 AND workspace_id = $2 UNION ALL SELECT igroup FROM email_to_igroup WHERE email = $3",
|
||||
username,
|
||||
w_id,
|
||||
email
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?
|
||||
.into_iter().filter_map(|x| x)
|
||||
.collect();
|
||||
Ok(groups)
|
||||
}
|
||||
pub async fn is_owner_of_path(
|
||||
authed: ApiAuthed,
|
||||
Path((_w_id, path)): Path<(String, StripPath)>,
|
||||
@@ -1692,10 +1750,8 @@ async fn create_user(
|
||||
|
||||
nu.email = nu.email.to_lowercase();
|
||||
|
||||
if nu.email == SUPERADMIN_SECRET_EMAIL {
|
||||
return Err(Error::BadRequest(
|
||||
"The superadmin email is a reserved email".into(),
|
||||
));
|
||||
if nu.email == SUPERADMIN_SECRET_EMAIL || nu.email == SUPERADMIN_NOTIFICATION_EMAIL {
|
||||
return Err(Error::BadRequest("This email address is reserved".into()));
|
||||
}
|
||||
|
||||
check_nb_of_user(&db).await?;
|
||||
@@ -2777,6 +2833,24 @@ async fn update_username_in_workpsace<'c>(
|
||||
.unwrap();
|
||||
|
||||
// ---- resources----
|
||||
sqlx::query!(
|
||||
r#"UPDATE resource SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3"#,
|
||||
new_username,
|
||||
old_username,
|
||||
w_id
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"UPDATE resource_type SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3"#,
|
||||
new_username,
|
||||
old_username,
|
||||
w_id
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"UPDATE resource SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
|
||||
new_username,
|
||||
@@ -2980,6 +3054,15 @@ async fn update_username_in_workpsace<'c>(
|
||||
|
||||
// ---- folders ----
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE folder SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
|
||||
new_username,
|
||||
old_username,
|
||||
w_id
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE folder SET owners = ARRAY_REPLACE(owners, 'u/' || $2, 'u/' || $1) WHERE ('u/' || $2) = ANY(owners) AND workspace_id = $3",
|
||||
new_username,
|
||||
|
||||
@@ -9,20 +9,13 @@
|
||||
use regex::Regex;
|
||||
use sqlx::{Postgres, Transaction};
|
||||
use windmill_common::{
|
||||
auth::is_super_admin_email,
|
||||
error::{self, Error},
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
DB,
|
||||
};
|
||||
|
||||
pub async fn require_super_admin(db: &DB, email: &str) -> error::Result<()> {
|
||||
if email == SUPERADMIN_SECRET_EMAIL || email == SUPERADMIN_NOTIFICATION_EMAIL {
|
||||
return Ok(());
|
||||
}
|
||||
let is_admin = sqlx::query_scalar!("SELECT super_admin FROM password WHERE email = $1", email)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("fetching super admin: {e:#}")))?
|
||||
.unwrap_or(false);
|
||||
let is_admin = is_super_admin_email(db, email).await?;
|
||||
|
||||
if !is_admin {
|
||||
Err(Error::NotAuthorized(
|
||||
|
||||
@@ -81,10 +81,16 @@ async fn list_contextual_variables(
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ListVariableQuery {
|
||||
path_start: Option<String>,
|
||||
}
|
||||
|
||||
async fn list_variables(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(lq): Query<ListVariableQuery>,
|
||||
) -> JsonResult<Vec<ListableVariable>> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
@@ -97,10 +103,13 @@ async fn list_variables(
|
||||
from variable
|
||||
LEFT JOIN account ON variable.account = account.id AND account.workspace_id = $1
|
||||
LEFT JOIN resource ON resource.path = variable.path AND resource.workspace_id = $1
|
||||
WHERE variable.workspace_id = $1 AND variable.path NOT LIKE 'u/' || $2 || '/secret_arg/%' ORDER BY path",
|
||||
WHERE variable.workspace_id = $1 AND variable.path NOT LIKE 'u/' || $2 || '/secret_arg/%'
|
||||
AND variable.path LIKE $3 || '%'
|
||||
ORDER BY path",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.bind(&authed.username)
|
||||
.bind(&lq.path_start.unwrap_or_default())
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -523,7 +532,7 @@ async fn update_variable(
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE resource SET path = $1, value = $2 WHERE path = $3 AND workspace_id = $4",
|
||||
"UPDATE resource SET path = $1, value = $2, edited_at = now() WHERE path = $3 AND workspace_id = $4",
|
||||
npath,
|
||||
v,
|
||||
path,
|
||||
|
||||
@@ -1605,29 +1605,33 @@ async fn create_workspace(
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO resource (workspace_id, path, value, description, resource_type) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO resource (workspace_id, path, value, description, resource_type, created_by, edited_at) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -11,4 +11,7 @@ use serde::Deserialize;
|
||||
#[derive(Deserialize)]
|
||||
pub struct ListAppQuery {
|
||||
pub starred_only: Option<bool>,
|
||||
pub path_exact: Option<String>,
|
||||
pub path_start: Option<String>,
|
||||
pub include_draft_only: Option<bool>,
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user