Compare commits
24 Commits
v1.426.0
...
migration/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd25c4bf72 | ||
|
|
b031e2f48c | ||
|
|
c95b59d5da | ||
|
|
c1b779ebb5 | ||
|
|
7b31a900f6 | ||
|
|
2f9cc1498d | ||
|
|
6baa549cdb | ||
|
|
4aa269f71f | ||
|
|
7e5956e5a0 | ||
|
|
7a6d404a11 | ||
|
|
fd5d89b56a | ||
|
|
2ad08d9194 | ||
|
|
32428ed5f3 | ||
|
|
b20d83e58e | ||
|
|
6300bb6e71 | ||
|
|
f6d9cd062d | ||
|
|
3e34bc7956 | ||
|
|
045a52cded | ||
|
|
f4df3affa3 | ||
|
|
698375cb17 | ||
|
|
c7a327e1d8 | ||
|
|
7d605e88b0 | ||
|
|
1ce19166ed | ||
|
|
a0542dc853 |
11
.github/workflows/build-publish-rh-image.yml
vendored
11
.github/workflows/build-publish-rh-image.yml
vendored
@@ -3,7 +3,8 @@ env:
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
name: Build and publish windmill for RHEL9
|
||||
on: workflow_dispatch
|
||||
on:
|
||||
workflow_dispatch
|
||||
|
||||
permissions: write-all
|
||||
|
||||
@@ -64,7 +65,7 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,kafka
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -73,7 +74,7 @@ jobs:
|
||||
labels: |
|
||||
${{ steps.meta-ee-public.outputs.labels }}-amd64
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
|
||||
- name: Build and push publicly ee arm64
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
@@ -81,7 +82,7 @@ jobs:
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,kafka
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -107,7 +108,7 @@ jobs:
|
||||
run: |
|
||||
mv "${{ steps.extract-ee-amd64.outputs.destination }}/windmill" "${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9"
|
||||
mv "${{ steps.extract-ee-arm64.outputs.destination }}/windmill" "${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9"
|
||||
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RHEL9-amd64 build
|
||||
|
||||
2
.github/workflows/build-staging-image.yml
vendored
2
.github/workflows/build-staging-image.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,kafka
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
|
||||
tags: |
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
labels: |
|
||||
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
|
||||
mkdir frontend/build && cd backend
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,kafka
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core
|
||||
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
58
.github/workflows/build_windows_worker_.yml
vendored
58
.github/workflows/build_windows_worker_.yml
vendored
@@ -1,58 +0,0 @@
|
||||
name: Build windows executable for this branch
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
SQLX_OFFLINE: true
|
||||
DISABLE_EMBEDDING: true
|
||||
RUST_LOG: info
|
||||
|
||||
jobs:
|
||||
cargo_build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Read EE repo commit hash
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ee_repo_ref = Get-Content .\backend\ee-repo-ref.txt
|
||||
echo "ee_repo_ref=$ee_repo_ref" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
|
||||
- name: Checkout windmill-ee-private repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: windmill-labs/windmill-ee-private
|
||||
path: ./windmill-ee-private
|
||||
ref: ${{ env.ee_repo_ref }}
|
||||
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Substitute EE code
|
||||
shell: bash
|
||||
run: |
|
||||
./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
|
||||
|
||||
- name: Cargo build windows
|
||||
timeout-minutes: 90
|
||||
run: |
|
||||
vcpkg.exe install openssl-windows:x64-windows
|
||||
vcpkg.exe install openssl:x64-windows-static
|
||||
vcpkg.exe integrate install
|
||||
$env:VCPKGRS_DYNAMIC=1
|
||||
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
|
||||
mkdir frontend/build && cd backend
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,kafka
|
||||
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windmill-ee-binary
|
||||
path: ./backend/target/release/windmill-ee.exe
|
||||
10
.github/workflows/docker-image.yml
vendored
10
.github/workflows/docker-image.yml
vendored
@@ -1,8 +1,10 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.event_name != 'pull_request' && github.repository ||
|
||||
IMAGE_NAME:
|
||||
${{ github.event_name != 'pull_request' && github.repository ||
|
||||
'windmill-labs/windmill-test' }}
|
||||
DEV_SHA: ${{ github.event_name != 'pull_request' && 'dev' || format('pr-{0}',
|
||||
DEV_SHA:
|
||||
${{ github.event_name != 'pull_request' && 'dev' || format('pr-{0}',
|
||||
github.event.number) }}
|
||||
|
||||
name: Build windmill:main
|
||||
@@ -138,7 +140,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,kafka
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
@@ -200,7 +202,7 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,kafka
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core
|
||||
PYTHON_IMAGE=python:3.12.2-slim-bookworm
|
||||
tags: |
|
||||
${{ steps.meta-ee-public-py312.outputs.tags }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,4 +6,4 @@ frontend/src/routes/test.svelte
|
||||
CaddyfileRemoteMalo
|
||||
*.swp
|
||||
**/.idea/
|
||||
.direnv
|
||||
.direnv
|
||||
|
||||
184
CHANGELOG.md
184
CHANGELOG.md
@@ -1,189 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.426.0](https://github.com/windmill-labs/windmill/compare/v1.425.1...v1.426.0) (2024-11-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add mode (permissions) option to files in ansible ([#4724](https://github.com/windmill-labs/windmill/issues/4724)) ([5e10782](https://github.com/windmill-labs/windmill/commit/5e107827790292d89e51d81138eeef2b5b23a9c2))
|
||||
* kafka triggers ([#4713](https://github.com/windmill-labs/windmill/issues/4713)) ([88b8ffa](https://github.com/windmill-labs/windmill/commit/88b8ffab907e662ccca612bf01e6a228566522d2))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not mount critical alerts modal if user is neither superadmin nor workspace admin ([#4731](https://github.com/windmill-labs/windmill/issues/4731)) ([180809d](https://github.com/windmill-labs/windmill/commit/180809d3466fb62d6692c45cbb012f158f83528f))
|
||||
|
||||
## [1.425.1](https://github.com/windmill-labs/windmill/compare/v1.425.0...v1.425.1) (2024-11-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* revert bool to text support in pg ([#4727](https://github.com/windmill-labs/windmill/issues/4727)) ([17d8933](https://github.com/windmill-labs/windmill/commit/17d893315bd86942c7895cdb3e9c3ab34977b258))
|
||||
|
||||
## [1.425.0](https://github.com/windmill-labs/windmill/compare/v1.424.0...v1.425.0) (2024-11-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Handle `pip install` by `uv` ([#4517](https://github.com/windmill-labs/windmill/issues/4517)) ([f240d13](https://github.com/windmill-labs/windmill/commit/f240d1322a3c1669dfb69ebda6d05effcca26ae3))
|
||||
* **monitoring:** workspace critical alerts ([#4684](https://github.com/windmill-labs/windmill/issues/4684)) ([c32038a](https://github.com/windmill-labs/windmill/commit/c32038a76d9d00703d8b865af72f083ac43414e3))
|
||||
|
||||
## [1.424.0](https://github.com/windmill-labs/windmill/compare/v1.423.2...v1.424.0) (2024-11-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow setting password and login type from superadmin UI ([2a1bff3](https://github.com/windmill-labs/windmill/commit/2a1bff3160b65eadba399229b5f53950ec2c0d48))
|
||||
* **backend:** monitor minimal version of living workers ([#4704](https://github.com/windmill-labs/windmill/issues/4704)) ([50ff183](https://github.com/windmill-labs/windmill/commit/50ff183baeaa2a39c2b0188ee8dd6172b08ae801))
|
||||
* Support mistral anthropic for ai ([#4692](https://github.com/windmill-labs/windmill/issues/4692)) ([556b4a4](https://github.com/windmill-labs/windmill/commit/556b4a41a1d010bb8a1796f485d343c1e412d278))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add countCompletedJobs api ([2a78359](https://github.com/windmill-labs/windmill/commit/2a78359af7b8e4700634c2ad2745c1d14d2da4f7))
|
||||
* add queue_couts api ([10b6b1d](https://github.com/windmill-labs/windmill/commit/10b6b1dc04cb2b2d4ec5ceb26a5dbd2ac7bd1394))
|
||||
* autoscaling when count < min worker set to min_workers ([180bb82](https://github.com/windmill-labs/windmill/commit/180bb826436f9960def8c6cf3e9692714ffdf917))
|
||||
* deployment callbacks have a concurrency limit of 1 on same path ([c44fc4f](https://github.com/windmill-labs/windmill/commit/c44fc4f5ab8bcb5dffec08d5ed4ccd61feb1cb13))
|
||||
|
||||
## [1.423.2](https://github.com/windmill-labs/windmill/compare/v1.423.1...v1.423.2) (2024-11-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix intempestive expr type change in flow transform ([84bd06e](https://github.com/windmill-labs/windmill/commit/84bd06e90650e949c11e2b09447a7bad1ab60a95))
|
||||
|
||||
## [1.423.1](https://github.com/windmill-labs/windmill/compare/v1.423.0...v1.423.1) (2024-11-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **autoscaling:** autoscaling thresholds to be >= and not > ([de00944](https://github.com/windmill-labs/windmill/commit/de00944f09699ff33a382f5a14f515ccf90b2454))
|
||||
|
||||
## [1.423.0](https://github.com/windmill-labs/windmill/compare/v1.422.1...v1.423.0) (2024-11-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* s3 input available for public apps ([#4685](https://github.com/windmill-labs/windmill/issues/4685)) ([1671005](https://github.com/windmill-labs/windmill/commit/167100510032ab53cd609fb2c7629e67faceb093))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* prevent underflow for autoscaling scalein ([5e9870a](https://github.com/windmill-labs/windmill/commit/5e9870a8a993032ec7d45c62e0023008da42c74a))
|
||||
* support multiple pip-extra-index-url with commas ([50861fc](https://github.com/windmill-labs/windmill/commit/50861fccc0cd86d4c76120c3306adf94f375330e))
|
||||
|
||||
## [1.422.1](https://github.com/windmill-labs/windmill/compare/v1.422.0...v1.422.1) (2024-11-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix password inputs ([e5e174a](https://github.com/windmill-labs/windmill/commit/e5e174ae9516f4c6b94ceb6e258b467f5c9a1f1a))
|
||||
|
||||
## [1.422.0](https://github.com/windmill-labs/windmill/compare/v1.421.2...v1.422.0) (2024-11-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* expandable subflows in flows ([#4683](https://github.com/windmill-labs/windmill/issues/4683)) ([d44976f](https://github.com/windmill-labs/windmill/commit/d44976f35e45ade510d1ec220b5a1503e11f3db9))
|
||||
* **frontend:** critical alerts UI ([#4653](https://github.com/windmill-labs/windmill/issues/4653)) ([d9148ea](https://github.com/windmill-labs/windmill/commit/d9148eaa78680a93d81d71847a7df67e01f3c110))
|
||||
|
||||
## [1.421.2](https://github.com/windmill-labs/windmill/compare/v1.421.1...v1.421.2) (2024-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bash:** correctly propagate exit errors ([8bc9a02](https://github.com/windmill-labs/windmill/commit/8bc9a021a88391147c9170f56b5a0edddd55bc7d))
|
||||
|
||||
## [1.421.1](https://github.com/windmill-labs/windmill/compare/v1.421.0...v1.421.1) (2024-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **python-client:** fix small break params of write_s3_file ([3ea12f1](https://github.com/windmill-labs/windmill/commit/3ea12f1821500e8b84549b892e3e1bb56a6ace4b))
|
||||
|
||||
## [1.421.0](https://github.com/windmill-labs/windmill/compare/v1.420.1...v1.421.0) (2024-11-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* http custom routes for static assets ([#4666](https://github.com/windmill-labs/windmill/issues/4666)) ([dc8bd6d](https://github.com/windmill-labs/windmill/commit/dc8bd6d2b5d6f5cd8521f9034853175ec78d5639))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve nested schema editor field change ([a3feca7](https://github.com/windmill-labs/windmill/commit/a3feca719799ea2bf08f2b49350e6b732a24abf4))
|
||||
|
||||
## [1.420.1](https://github.com/windmill-labs/windmill/compare/v1.420.0...v1.420.1) (2024-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve prop filtering on flow prop picker ([0501ad8](https://github.com/windmill-labs/windmill/commit/0501ad8a99f6f7d9fd26c996b754e8afe8f958b1))
|
||||
|
||||
## [1.420.0](https://github.com/windmill-labs/windmill/compare/v1.419.0...v1.420.0) (2024-11-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** detect expr in flow input transform + filter right panel based on expr ([#4651](https://github.com/windmill-labs/windmill/issues/4651)) ([e9b7dca](https://github.com/windmill-labs/windmill/commit/e9b7dca20387e775fa50aaecd832890251582cf9))
|
||||
* **frontend:** nodes from flow can be connected directly in expr input through a plug icon ([#4652](https://github.com/windmill-labs/windmill/issues/4652)) ([ceaf56c](https://github.com/windmill-labs/windmill/commit/ceaf56c21ee2e548bc93859f9e0303e53b25b241))
|
||||
|
||||
## [1.419.0](https://github.com/windmill-labs/windmill/compare/v1.418.0...v1.419.0) (2024-11-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add full-text search on windmill service logs ([#4576](https://github.com/windmill-labs/windmill/issues/4576)) ([77735d8](https://github.com/windmill-labs/windmill/commit/77735d859cfee9204f67a8e4f9885228d657a41d))
|
||||
* show path in flow script picker ([#4574](https://github.com/windmill-labs/windmill/issues/4574)) ([8e27392](https://github.com/windmill-labs/windmill/commit/8e27392afacbb725aaf9f9f892ab8a6171b59ce5))
|
||||
* websocket authentication ([#4635](https://github.com/windmill-labs/windmill/issues/4635)) ([4dda0fb](https://github.com/windmill-labs/windmill/commit/4dda0fb8cd8262ad3a2ab2b9d27e7043ac3bb891))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* clarify error messages when job timeout or cancelled with more details ([771d740](https://github.com/windmill-labs/windmill/commit/771d740701902166f8b4e3f77aa9c5579237cb15))
|
||||
* **cli:** improve handling of deleted items on windows ([9a8dcc9](https://github.com/windmill-labs/windmill/commit/9a8dcc9a250caefa0b7c9523e1321599b7471c8b))
|
||||
* display logs in native mode when script fails ([#4655](https://github.com/windmill-labs/windmill/issues/4655)) ([7578ceb](https://github.com/windmill-labs/windmill/commit/7578cebaf92e729a26fd665e4b6f8357d34f59eb))
|
||||
* **frontend:** arg input json handling when the value is not of the same type as schema ([#4479](https://github.com/windmill-labs/windmill/issues/4479)) ([8d8156b](https://github.com/windmill-labs/windmill/commit/8d8156bd0773da3ddec81c46ad5fda114ecd3dda))
|
||||
* **frontend:** improve flow prop picker design ([323912c](https://github.com/windmill-labs/windmill/commit/323912c73c18d3bb6d136f2e6458389270364a0e))
|
||||
|
||||
## [1.418.0](https://github.com/windmill-labs/windmill/compare/v1.417.3...v1.418.0) (2024-11-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** improve and simplify scheduled poll flows ([#4560](https://github.com/windmill-labs/windmill/issues/4560)) ([22ab51e](https://github.com/windmill-labs/windmill/commit/22ab51e9914376b47827f0be66708deef3a37767))
|
||||
* **oauth:** add snowflake oauth support ([#4622](https://github.com/windmill-labs/windmill/issues/4622)) ([693b7a4](https://github.com/windmill-labs/windmill/commit/693b7a4fd4da0e2b14cb6c8ea19537aa600225be))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **prometheus:** improve queue_count when tags have no more jobs ([09156b6](https://github.com/windmill-labs/windmill/commit/09156b65c2a0f0ac59fce1dede2b68b790323c49))
|
||||
|
||||
## [1.417.3](https://github.com/windmill-labs/windmill/compare/v1.417.2...v1.417.3) (2024-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** improve pulling instance with folderPerInstance ([1e7909e](https://github.com/windmill-labs/windmill/commit/1e7909e95931e1e121cb3767bad093239254935d))
|
||||
|
||||
## [1.417.2](https://github.com/windmill-labs/windmill/compare/v1.417.1...v1.417.2) (2024-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** improve pulling instance with folderPerInstance ([00748f4](https://github.com/windmill-labs/windmill/commit/00748f43feab4f28f0dd2682e3dc8ccc15a5f49f))
|
||||
|
||||
## [1.417.1](https://github.com/windmill-labs/windmill/compare/v1.417.0...v1.417.1) (2024-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* missing opts for pull and push instance configs ([#4630](https://github.com/windmill-labs/windmill/issues/4630)) ([e92a338](https://github.com/windmill-labs/windmill/commit/e92a338432d2732e6be74c3604b9fa95239e1ca3))
|
||||
|
||||
## 1.417.0 (2024-11-03)
|
||||
|
||||
* feat(cli): instance yaml files inside the instance prefix folders (#4627)
|
||||
|
||||
## [1.416.2](https://github.com/windmill-labs/windmill/compare/v1.416.1...v1.416.2) (2024-11-02)
|
||||
|
||||
|
||||
|
||||
@@ -175,9 +175,9 @@ RUN /usr/local/bin/python3 -m pip install pip-tools
|
||||
COPY --from=builder /frontend/build /static_frontend
|
||||
COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
|
||||
|
||||
COPY --from=denoland/deno:2.0.4 --chmod=755 /usr/bin/deno /usr/bin/deno
|
||||
COPY --from=denoland/deno:2.0.2 --chmod=755 /usr/bin/deno /usr/bin/deno
|
||||
|
||||
COPY --from=oven/bun:1.1.34 /usr/local/bin/bun /usr/bin/bun
|
||||
COPY --from=oven/bun:1.1.32 /usr/local/bin/bun /usr/bin/bun
|
||||
|
||||
COPY --from=php:8.3.7-cli /usr/local/bin/php /usr/bin/php
|
||||
COPY --from=composer:2.7.6 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
@@ -355,8 +355,6 @@ you to have it being synced automatically everyday.
|
||||
See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all
|
||||
running options.
|
||||
|
||||
Using [Nix](./frontend/README_DEV.md#nix).
|
||||
|
||||
### only Frontend
|
||||
This will use the backend of <https://app.windmill.dev> but your own frontend
|
||||
with hot-code reloading. Note that you will need to use a username / password login due to CSRF checks using a different auth provider.
|
||||
|
||||
2
backend/.gitignore
vendored
2
backend/.gitignore
vendored
@@ -2,7 +2,7 @@ target/
|
||||
.env
|
||||
oauth.json
|
||||
oauth2.json
|
||||
windmill-api/openapi-deref.yaml
|
||||
tracing.folded
|
||||
heaptrack*
|
||||
index/
|
||||
windmill-api/openapi-*.*
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE alerts SET acknowledged_workspace = true, acknowledged = true WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "00588a40dde5189ac1c61505f17acb0f4c244c60477427505bf5bd1b104d3bf9"
|
||||
}
|
||||
14
backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json
generated
Normal file
14
backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO alerts (alert_type, message) VALUES ('recovered_critical_error', $1)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\", static_asset_config as \"static_asset_config: _\" FROM http_trigger WHERE workspace_id = $1",
|
||||
"query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\" FROM http_trigger WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -65,11 +65,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "static_asset_config: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -87,9 +82,8 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c9930fcfe79541af570eace58ba7e15a0816a6b4fd036cf7b991a210654b2633"
|
||||
"hash": "02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441"
|
||||
}
|
||||
16
backend/.sqlx/query-034583442e6f8ae38d6c4e4aac26f17c8d9d0e657f28276228fc90d3e22e1304.json
generated
Normal file
16
backend/.sqlx/query-034583442e6f8ae38d6c4e4aac26f17c8d9d0e657f28276228fc90d3e22e1304.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET openai_resource_path = $1, code_completion_enabled = $2 WHERE workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "034583442e6f8ae38d6c4e4aac26f17c8d9d0e657f28276228fc90d3e22e1304"
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO alerts (alert_type, message, acknowledged, acknowledged_workspace, workspace_id, resource)\n VALUES ('critical_error', $1, $2, $3, $4, $5)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "044e2b428ee6e2dd4543c87ad8835e239cf7567d18b8b3fa6608ea3a9d206ca7"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $11)\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout, raw_code, raw_lock, raw_flow, flow_status)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14, $15, $16, $17, $18 FROM uuid_table) \n RETURNING id",
|
||||
"query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $11)\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14 FROM uuid_table) \n RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -72,16 +72,12 @@
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Text",
|
||||
"Text",
|
||||
"Jsonb",
|
||||
"Jsonb"
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284"
|
||||
"hash": "0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18"
|
||||
}
|
||||
14
backend/.sqlx/query-0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a.json
generated
Normal file
14
backend/.sqlx/query-0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM healthchecks WHERE check_type = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET ai_resource = $1, code_completion_enabled = $2 WHERE workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1610c79b8d238e3a35d795f34a2a0100b933b7f199d41cc2f554d57c811d55f3"
|
||||
}
|
||||
@@ -65,8 +65,8 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "ai_resource",
|
||||
"type_info": "Jsonb"
|
||||
"name": "openai_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
@@ -117,11 +117,6 @@
|
||||
"ordinal": 22,
|
||||
"name": "deploy_ui",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -152,7 +147,6 @@
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n -- slack_team_id, \n -- slack_name, \n -- slack_command_script, \n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\", \n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\", \n webhook, \n deploy_to, \n error_handler, \n ai_resource, \n code_completion_enabled, \n error_handler_extra_args, \n error_handler_muted_on_cancel, \n large_file_storage, \n git_sync,\n default_app,\n default_scripts,\n workspace.name\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1",
|
||||
"query": "SELECT\n -- slack_team_id, \n -- slack_name, \n -- slack_command_script, \n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\", \n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\", \n webhook, \n deploy_to, \n error_handler, \n openai_resource_path, \n code_completion_enabled, \n error_handler_extra_args, \n error_handler_muted_on_cancel, \n large_file_storage, \n git_sync,\n default_app,\n default_scripts,\n workspace.name\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -35,8 +35,8 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "ai_resource",
|
||||
"type_info": "Jsonb"
|
||||
"name": "openai_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
@@ -102,5 +102,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "0331a81262e2d3c1bcfaeb64617b11eb68ab4599d64e5e5af639a9ac5d791fd0"
|
||||
"hash": "188534f4b29f6461b1a6214d060f183c830b19a403ebb7b8be55a691675010c3"
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n flow_status AS \"flow_status!: Json<Box<RawValue>>\",\n raw_flow->'modules'->(flow_status->'step')::int AS \"module: Json<Box<RawValue>>\"\n FROM queue WHERE id = $1 AND workspace_id = $2 LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "flow_status!: Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "module: Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1e7ce0c140410ae799f9c0c5772e4be4506bfd238c88f1b1c3ddf39b29071446"
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM kafka_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "2139f1fb1877294bbf55d786000c5c32f582a3911efcf88e437aa90d7d5a49b5"
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO alerts (alert_type, message, acknowledged, acknowledged_workspace, workspace_id, resource)\n VALUES ('recovered_critical_error', $1, $2, $3, $4, $5)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "28987898c7e4b172d466bf08f33c2da733e71f8a253b0122c17b9e021919809e"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM completed_job\n WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, alert_type, message, created_at, COALESCE(acknowledged, false) AS acknowledged, workspace_id\n FROM alerts\n WHERE COALESCE(acknowledged, false) = $1\n ORDER BY created_at DESC\n LIMIT $2 OFFSET $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "alert_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "message",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "acknowledged",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "344b3a5d9683273a956b5156fed5ab9fdff1a7252198e4ae290d8c8f937ff177"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE password SET login_type = $1 WHERE email = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "349396e8fdd96d45875110bc06767e6eb876792ec8f83e9b03c2fb46bb12e0b9"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM kafka_trigger WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3997dcf2c11817e59bf50fd896381d870b847a8ba07e6197212fdf85ec901b09"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT raw_flow->'modules'->($1)::text->'value'->>'type' = 'flow' FROM queue WHERE id = $2 LIMIT 1",
|
||||
"query": "SELECT raw_flow->'modules'->($1)->'value'->>'type' = 'flow' FROM queue WHERE id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -19,5 +19,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "de1abe57b6aa61155f747a3bcb98359f70eade6654b5a884915f07f3ef3fe15e"
|
||||
"hash": "3e539fef054ad31bc1736e27276087775a721a6ee7ae35b03fd4ce3563ea3838"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE kafka_trigger SET enabled = FALSE, error = $1, server_id = NULL, last_server_ping = NULL WHERE workspace_id = $2 AND path = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3e64c894c89ef82c4527180b82c4e82c1b7060ba0ca288dc6a7c7afcd76212e8"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE alerts SET acknowledged = true, acknowledged_workspace = true WHERE resource = $1 AND alert_type = 'critical_error'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4a3a8207627418ba7b7eabaccd7ec72884f2a1eacf0ab0e0c6ccbf56c654c14a"
|
||||
}
|
||||
14
backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json
generated
Normal file
14
backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO alerts (alert_type, message) VALUES ('critical_error', $1)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f"
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, alert_type, message, created_at, COALESCE(acknowledged, false) AS acknowledged, workspace_id\n FROM alerts\n ORDER BY created_at DESC\n LIMIT $1 OFFSET $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "alert_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "message",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "acknowledged",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "4d30c5a2894d655741d167f5589f5816583f0bae78a0dd3270dedb377dfa944a"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE http_trigger \n SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, static_asset_config = $7, edited_by = $8, email = $9, is_async = $10, requires_auth = $11, edited_at = now() \n WHERE workspace_id = $12 AND path = $13",
|
||||
"query": "UPDATE http_trigger \n SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, edited_by = $7, email = $8, is_async = $9, requires_auth = $10, edited_at = now() \n WHERE workspace_id = $11 AND path = $12",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -24,7 +24,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
@@ -35,5 +34,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "487d377e2df67fc3ea39d183ba9f99d45828d7c8e0ff10c5d74c454472e0493c"
|
||||
"hash": "4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea"
|
||||
}
|
||||
108
backend/.sqlx/query-4e9668a46bad9e82baa51422946d373b18b6577198df7545c94bd19be3446775.json
generated
Normal file
108
backend/.sqlx/query-4e9668a46bad9e82baa51422946d373b18b6577198df7545c94bd19be3446775.json
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO websocket_trigger (workspace_id, path, url, script_path, is_flow, enabled, filters, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, now()) RETURNING *",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "filters",
|
||||
"type_info": "JsonbArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"JsonbArray",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4e9668a46bad9e82baa51422946d373b18b6577198df7545c94bd19be3446775"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as \"http_method: _\", edited_by, email, edited_at, extra_perms, is_async, requires_auth, static_asset_config as \"static_asset_config: _\"\n FROM http_trigger\n WHERE workspace_id = $1 AND path = $2",
|
||||
"query": "SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as \"http_method: _\", edited_by, email, edited_at, extra_perms, is_async, requires_auth\n FROM http_trigger\n WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -80,11 +80,6 @@
|
||||
"ordinal": 12,
|
||||
"name": "requires_auth",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "static_asset_config: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -106,9 +101,8 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f904702536c106b0e5da8facae119c6af887c49a29ae44b3a95350ff27fb1ccf"
|
||||
"hash": "4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491"
|
||||
}
|
||||
98
backend/.sqlx/query-5303cb9dd5903aa4791ef8e5e5881a50a832e65c8c9632e2e12cd9c2747f2fc7.json
generated
Normal file
98
backend/.sqlx/query-5303cb9dd5903aa4791ef8e5e5881a50a832e65c8c9632e2e12cd9c2747f2fc7.json
generated
Normal file
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT *\n FROM websocket_trigger\n WHERE enabled IS TRUE AND (server_id IS NULL OR last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "filters",
|
||||
"type_info": "JsonbArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "5303cb9dd5903aa4791ef8e5e5881a50a832e65c8c9632e2e12cd9c2747f2fc7"
|
||||
}
|
||||
@@ -65,8 +65,8 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "ai_resource",
|
||||
"type_info": "Jsonb"
|
||||
"name": "openai_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
@@ -117,11 +117,6 @@
|
||||
"ordinal": 22,
|
||||
"name": "deploy_ui",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -152,7 +147,6 @@
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, static_asset_config = $5, edited_by = $6, email = $7, is_async = $8, requires_auth = $9, edited_at = now() \n WHERE workspace_id = $10 AND path = $11",
|
||||
"query": "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, edited_by = $5, email = $6, is_async = $7, requires_auth = $8, edited_at = now() \n WHERE workspace_id = $9 AND path = $10",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -22,7 +22,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
@@ -33,5 +32,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7113d7cc72e44e4b7e01b69cc18cbe7b0399cf8ec0e9e6d2b05ceef589c432df"
|
||||
"hash": "55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT created_by, CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM queue \n LEFT JOIN job_logs ON job_logs.job_id = queue.id \n WHERE queue.id = $1 AND queue.workspace_id = $2",
|
||||
"query": "SELECT created_by, CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, job_logs.log_offset, job_logs.log_file_index\n FROM queue \n LEFT JOIN job_logs ON job_logs.job_id = queue.id \n WHERE queue.id = $1 AND queue.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -33,9 +33,9 @@
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "b2c30137b3b64f8c3f6aea062e8687e119599641beee0a0d6c89c5d90e82d0db"
|
||||
"hash": "57111a97ff906fd79ecd8a571212cc4fea9cb9c16fe950f082ad5469bfeefc82"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE kafka_trigger SET kafka_resource_path = $1, group_id = $2, topics = $3, script_path = $4, path = $5, is_flow = $6, edited_by = $7, email = $8, edited_at = now(), server_id = NULL, last_server_ping = NULL, error = NULL\n WHERE workspace_id = $9 AND path = $10",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5962733746c81480abe9ab3a6ccf5664130ae7500279055c08db42d67b2822f6"
|
||||
}
|
||||
15
backend/.sqlx/query-6268eabd561502a44e273d6391102278974623f7a7bcad6891d7abadf4d3ea03.json
generated
Normal file
15
backend/.sqlx/query-6268eabd561502a44e273d6391102278974623f7a7bcad6891d7abadf4d3ea03.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET openai_resource_path = NULL, code_completion_enabled = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6268eabd561502a44e273d6391102278974623f7a7bcad6891d7abadf4d3ea03"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE alerts\n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true\n ELSE acknowledged_workspace\n END\n WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET ai_resource = NULL, code_completion_enabled = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6940ddc5ee8d2a1048213d46f52d964b199604ba66dedbe9f89cea727d726a2d"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM kafka_trigger WHERE path = $1 AND workspace_id = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "6d418b5cd7a4df54cfe3ec06e2a957c87f2307a218115ca749ea0e71eca2002e"
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE kafka_trigger SET last_server_ping = now(), error = $1 WHERE workspace_id = $2 AND path = $3 AND server_id = $4 AND enabled IS TRUE RETURNING 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "6e70ebf078ac04a2933d2f83791973e8fc108d9f32be8a8501391052d76e191e"
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE kafka_trigger SET enabled = $1, email = $2, edited_by = $3, edited_at = now(), server_id = NULL, last_server_ping = NULL, error = NULL\n WHERE path = $4 AND workspace_id = $5 RETURNING 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "6eb076afcf11dba0dfe35ee108c64814f95dbe5376f0539195d24a8ca96ca3e2"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, alert_type, message, created_at, COALESCE(acknowledged_workspace, false) AS acknowledged, workspace_id\n FROM alerts\n WHERE workspace_id = $1\n ORDER BY created_at DESC\n LIMIT $2 OFFSET $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "alert_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "message",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "acknowledged",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "777e7084edf9a5d14f299ff479ae43c8ead47a6a531d4b031a1342c9b2f16506"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE alerts \n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $1::text IS NOT NULL THEN true\n ELSE acknowledged_workspace\n END\n WHERE ($1::text IS NOT NULL AND workspace_id = $1)\n OR ($1::text IS NULL)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3"
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, alert_type, message, created_at, COALESCE(acknowledged_workspace, false) AS acknowledged, workspace_id\n FROM alerts\n WHERE workspace_id = $1 AND COALESCE(acknowledged_workspace, false) = $2\n ORDER BY created_at DESC\n LIMIT $3 OFFSET $4",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "alert_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "message",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "acknowledged",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "7c5a29de07cbe42326a15d4d7fd9714c20b767508e27368be6de38d7b18a3a4d"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE alerts SET acknowledged = true",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a59fae29ebcc9aa53308b777ead3d2b652618ac454f139db738f4499cb4eb079"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM queue\n WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE websocket_trigger SET url = $1, script_path = $2, path = $3, is_flow = $4, filters = $5, initial_messages = $6, url_runnable_args = $7, edited_by = $8, email = $9, edited_at = now(), server_id = NULL, last_server_ping = NULL, error = NULL\n WHERE workspace_id = $10 AND path = $11",
|
||||
"query": "UPDATE websocket_trigger SET url = $1, script_path = $2, path = $3, is_flow = $4, filters = $5, edited_by = $6, email = $7, edited_at = now(), server_id = NULL, last_server_ping = NULL, error = NULL\n WHERE workspace_id = $8 AND path = $9",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -10,8 +10,6 @@
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"JsonbArray",
|
||||
"JsonbArray",
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
@@ -20,5 +18,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0b94bd4c98a11ca1b7e5e34dd1ee6fcb0b7a54ed4218fa3cf23cc929d009d50f"
|
||||
"hash": "acbf74cf3302bfcf7615285070d3f8958932bb8a2dda715f1b9152ab44442780"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT value\n FROM resource\n WHERE path = $1 AND workspace_id = $2",
|
||||
"query": "SELECT value\n FROM resource\n WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -19,5 +19,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "286fa00c088df146c08c1934556f06a4243c66787ab8759a8045e60effd2fb77"
|
||||
"hash": "acdaa5151f8f7f37bb8c8c5a7d146789887e47db9695fc26b1dfaedd735e1e60"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT wm_version FROM worker_ping WHERE wm_version != $1 AND ping_at > now() - interval '5 minutes'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "wm_version",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ad03e5acf10ef94abc37cb9f56b1775c67f075c8bc83458e8be2e242347218d6"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT flow_version.value AS \"value: sqlx::types::Json<Box<RawValue>>\" FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 AND flow.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "value: sqlx::types::Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a"
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n hostname,\n mode::text,\n worker_group,\n log_ts,\n file_path,\n ok_lines,\n err_lines,\n json_fmt\n FROM log_file\n WHERE log_ts > $1\n ORDER BY log_ts ASC LIMIT $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "hostname",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "mode",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "worker_group",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "log_ts",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "file_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "ok_lines",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "err_lines",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "json_fmt",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Timestamp",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "b5c839baab25c4dcdd503d380cf7a886242277cd50555f20b2e22e13942d2a3a"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, static_asset_config, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now())",
|
||||
"query": "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now())",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -27,12 +27,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "333b484ffa030dee08e7b1161fcbc48af411377d2d9f58f92fc9d5eacdf0fba1"
|
||||
"hash": "c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) as \"websocket_used!\", \n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) as \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\"",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "websocket_used!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "http_routes_used!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "kafka_used!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "cc8a71abdeccf0787695af32aa21fe73aba65cfdc8329c02b4d257de4d2d168a"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE websocket_trigger SET enabled = FALSE, error = $1, server_id = NULL, last_server_ping = NULL WHERE workspace_id = $2 AND path = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "cd33a9d63f4706a7e3b1e23cd0a4b2e3ecb30aae6510d1fcd08493b07c8b0952"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT ai_resource, code_completion_enabled FROM workspace_settings WHERE workspace_id = $1",
|
||||
"query": "SELECT openai_resource_path, code_completion_enabled FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "ai_resource",
|
||||
"type_info": "Jsonb"
|
||||
"name": "openai_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
@@ -24,5 +24,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "0230bd64d2b6719c3536a106e18a68c7b43b3a9a9efa92b5517132e9c0d8a25b"
|
||||
"hash": "ceb97024ebf1a1c00ea1ed4952f66b229ca4622c537cc252d8ed52b4d24270ee"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT ai_resource FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "ai_resource",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "ceda377fa534656ac12a7f41db77db1f054ec751855c8db7352b0e9c20b63ef8"
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE kafka_trigger SET server_id = $1, last_server_ping = now() WHERE enabled IS TRUE AND workspace_id = $2 AND path = $3 AND (server_id IS NULL OR last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') RETURNING true",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "d6615719bf8db4b333ed55c9a3c160ad1962668fc3305d8e80ae91ef73614a80"
|
||||
}
|
||||
28
backend/.sqlx/query-db24f1f6ef1e26b7de7926bd3f32d1fa673c4970c25ff9ad98f0fdf199801b53.json
generated
Normal file
28
backend/.sqlx/query-db24f1f6ef1e26b7de7926bd3f32d1fa673c4970c25ff9ad98f0fdf199801b53.json
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) as \"websocket_used!\", EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) as \"http_routes_used!\"",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "websocket_used!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "http_routes_used!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "db24f1f6ef1e26b7de7926bd3f32d1fa673c4970c25ff9ad98f0fdf199801b53"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n hostname,\n mode::text,\n worker_group,\n log_ts,\n file_path,\n ok_lines,\n err_lines,\n json_fmt\n FROM log_file\n ORDER BY log_ts ASC LIMIT $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "hostname",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "mode",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "worker_group",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "log_ts",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "file_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "ok_lines",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "err_lines",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "json_fmt",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "dd967c5983fa0ff05e2b320ad0e0b5a152784826cb8fb4381c1ffe228cb7feb6"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET mute_critical_alerts = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f22168826350797e88153b65a5b1e906a7868f34c062f154b2ee4f24c57aa5c3"
|
||||
}
|
||||
101
backend/.sqlx/query-f2baee15e6d1fecd6d2d7b39fda1b50a15ec8bd349f1081af54da5dc2f5e3021.json
generated
Normal file
101
backend/.sqlx/query-f2baee15e6d1fecd6d2d7b39fda1b50a15ec8bd349f1081af54da5dc2f5e3021.json
generated
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT *\n FROM websocket_trigger\n WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "filters",
|
||||
"type_info": "JsonbArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f2baee15e6d1fecd6d2d7b39fda1b50a15ec8bd349f1081af54da5dc2f5e3021"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT mute_critical_alerts FROM workspace_settings WHERE workspace_id = $1",
|
||||
"query": "SELECT openai_resource_path FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
"name": "openai_resource_path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -18,5 +18,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e05dbe046e846c092a96b6b0a9d872c721169d418586be2d15cee6b929049098"
|
||||
"hash": "f8b689ec3e09f14dc02ac9e9f98d252a14d3a2acfadcd9cd6ba27ca9799f4706"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\", static_asset_config as \"static_asset_config: _\" FROM http_trigger",
|
||||
"query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\" FROM http_trigger",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -65,11 +65,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "static_asset_config: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -85,9 +80,8 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "11b698f82a54aac68b3617047dfe2b18dd6da7d962118fee276af354218baac2"
|
||||
"hash": "fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183"
|
||||
}
|
||||
933
backend/Cargo.lock
generated
933
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.426.0"
|
||||
version = "1.416.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -29,7 +29,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.426.0"
|
||||
version = "1.416.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -62,7 +62,6 @@ jemalloc = ["windmill-common/jemalloc", "dep:tikv-jemallocator", "dep:tikv-jemal
|
||||
tantivy = ["dep:windmill-indexer", "windmill-api/tantivy"]
|
||||
sqlx = ["windmill-worker/sqlx"]
|
||||
deno_core = ["windmill-worker/deno_core", "dep:deno_core"]
|
||||
kafka = ["windmill-api/kafka"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@@ -155,7 +154,7 @@ tracing-appender = "^0"
|
||||
prometheus = { version = "^0", default-features = false }
|
||||
cookie = { version = "0.17.0" }
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
rust-embed = { version = "^6", features = ["interpolate-folder-path"] }
|
||||
rust-embed = "^6"
|
||||
mime_guess = "^2"
|
||||
hex = "^0"
|
||||
sql-builder = "^3"
|
||||
@@ -181,7 +180,6 @@ tokio-util = { version = "^0", features = ["io"] }
|
||||
json-pointer = "^0"
|
||||
itertools = "^0"
|
||||
regex = "^1"
|
||||
semver = "^1"
|
||||
|
||||
deno_fetch = "0.195.0"
|
||||
deno_tls = "0.158.0"
|
||||
@@ -267,7 +265,6 @@ tokio-native-tls = "^0"
|
||||
openssl = "=0.10"
|
||||
mail-parser = "^0"
|
||||
matchit = "=0.7.3"
|
||||
rdkafka = { version = "0.36.2", features = ["cmake-build", "ssl-vendored"] }
|
||||
|
||||
datafusion = "39.0.0"
|
||||
object_store = { version = "0.10.0", features = ["aws", "azure"] }
|
||||
|
||||
@@ -1 +1 @@
|
||||
7cdaac656fde3f3e267dfb9e94dda7f3843aa44e
|
||||
f136a2f499e0fe7c10c54c79488851980d796eb2
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP TABLE job_params;
|
||||
DROP TABLE job_args;
|
||||
DROP TABLE completed_jobs_result;
|
||||
@@ -0,0 +1,25 @@
|
||||
-- Add up migration script here
|
||||
-- Add down migration script here
|
||||
-- Add up migration script here
|
||||
CREATE TABLE job_params (
|
||||
id UUID PRIMARY KEY,
|
||||
raw_code TEXT,
|
||||
raw_flow jsonb NULL,
|
||||
tag VARCHAR(50),
|
||||
workspace_id VARCHAR(50)
|
||||
);
|
||||
|
||||
-- Add up migration script here
|
||||
CREATE TABLE job_args (
|
||||
id UUID PRIMARY KEY,
|
||||
args JSONB,
|
||||
tag VARCHAR(50),
|
||||
workspace_id VARCHAR(50)
|
||||
);
|
||||
|
||||
CREATE TABLE completed_jobs_result (
|
||||
id UUID PRIMARY KEY,
|
||||
result JSONB,
|
||||
tag VARCHAR(50),
|
||||
workspace_id VARCHAR(50)
|
||||
);
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE websocket_trigger DROP COLUMN initial_messages, DROP COLUMN url_runnable_args;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE websocket_trigger ADD COLUMN initial_messages JSONB[] DEFAULT '{}', ADD COLUMN url_runnable_args JSONB DEFAULT '{}';
|
||||
@@ -1,7 +0,0 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE workspace_settings
|
||||
ALTER COLUMN ai_resource TYPE text
|
||||
USING ai_resource->>'path';
|
||||
|
||||
ALTER TABLE workspace_settings
|
||||
RENAME COLUMN ai_resource to openai_resource_path;
|
||||
@@ -1,7 +0,0 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE workspace_settings
|
||||
ALTER COLUMN openai_resource_path TYPE jsonb
|
||||
USING jsonb_build_object('provider', 'openai', 'path', openai_resource_path);
|
||||
|
||||
ALTER TABLE workspace_settings
|
||||
RENAME COLUMN openai_resource_path TO ai_resource;
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE alerts DROP COLUMN acknowledged;
|
||||
@@ -1,9 +0,0 @@
|
||||
-- Step 1: Add the new column 'acknowledged' to the 'alerts' table
|
||||
ALTER TABLE alerts
|
||||
ADD COLUMN acknowledged BOOLEAN DEFAULT NULL;
|
||||
|
||||
-- Step 2: Update all existing rows to set 'acknowledged' to true
|
||||
-- we don't want to pop up notifications to all users after migrations
|
||||
-- but only show new alerts from the point of the upgrade
|
||||
UPDATE alerts
|
||||
SET acknowledged = TRUE;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE http_trigger DROP COLUMN static_asset_config;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE http_trigger ADD COLUMN static_asset_config JSONB;
|
||||
@@ -1,4 +0,0 @@
|
||||
ALTER TABLE alerts DROP COLUMN workspace_id;
|
||||
ALTER TABLE alerts DROP COLUMN acknowledged_workspace;
|
||||
ALTER TABLE alerts DROP COLUMN resource;
|
||||
ALTER TABLE workspace_settings DROP COLUMN mute_critical_alerts;
|
||||
@@ -1,4 +0,0 @@
|
||||
ALTER TABLE alerts ADD COLUMN workspace_id TEXT DEFAULT NULL;
|
||||
ALTER TABLE alerts ADD COLUMN acknowledged_workspace BOOL DEFAULT NULL;
|
||||
ALTER TABLE alerts ADD COLUMN resource TEXT DEFAULT NULL;
|
||||
ALTER TABLE workspace_settings ADD COLUMN mute_critical_alerts BOOL DEFAULT NULL;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add down migration script here
|
||||
DROP TABLE kafka_trigger;
|
||||
@@ -1,69 +0,0 @@
|
||||
-- Add up migration script here
|
||||
-- Add up migration script here
|
||||
|
||||
CREATE TABLE kafka_trigger (
|
||||
path VARCHAR(255) NOT NULL,
|
||||
kafka_resource_path VARCHAR(255) NOT NULL,
|
||||
topics VARCHAR(255)[] NOT NULL,
|
||||
group_id VARCHAR(255) NOT NULL,
|
||||
script_path VARCHAR(255) NOT NULL,
|
||||
is_flow BOOLEAN NOT NULL,
|
||||
workspace_id VARCHAR(50) NOT NULL,
|
||||
edited_by VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
extra_perms JSONB NOT NULL DEFAULT '{}',
|
||||
server_id VARCHAR(50) NULL,
|
||||
last_server_ping TIMESTAMPTZ NULL,
|
||||
error TEXT NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
PRIMARY KEY (path, workspace_id)
|
||||
);
|
||||
|
||||
GRANT ALL ON kafka_trigger TO windmill_user;
|
||||
GRANT ALL ON kafka_trigger TO windmill_admin;
|
||||
|
||||
ALTER TABLE kafka_trigger ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY admin_policy ON kafka_trigger FOR ALL TO windmill_admin USING (true);
|
||||
|
||||
CREATE POLICY see_folder_extra_perms_user_select ON kafka_trigger FOR SELECT TO windmill_user
|
||||
USING (SPLIT_PART(kafka_trigger.path, '/', 1) = 'f' AND SPLIT_PART(kafka_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]));
|
||||
CREATE POLICY see_folder_extra_perms_user_insert ON kafka_trigger FOR INSERT TO windmill_user
|
||||
WITH CHECK (SPLIT_PART(kafka_trigger.path, '/', 1) = 'f' AND SPLIT_PART(kafka_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
CREATE POLICY see_folder_extra_perms_user_update ON kafka_trigger FOR UPDATE TO windmill_user
|
||||
USING (SPLIT_PART(kafka_trigger.path, '/', 1) = 'f' AND SPLIT_PART(kafka_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
CREATE POLICY see_folder_extra_perms_user_delete ON kafka_trigger FOR DELETE TO windmill_user
|
||||
USING (SPLIT_PART(kafka_trigger.path, '/', 1) = 'f' AND SPLIT_PART(kafka_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
|
||||
CREATE POLICY see_own ON kafka_trigger FOR ALL TO windmill_user
|
||||
USING (SPLIT_PART(kafka_trigger.path, '/', 1) = 'u' AND SPLIT_PART(kafka_trigger.path, '/', 2) = current_setting('session.user'));
|
||||
CREATE POLICY see_member ON kafka_trigger FOR ALL TO windmill_user
|
||||
USING (SPLIT_PART(kafka_trigger.path, '/', 1) = 'g' AND SPLIT_PART(kafka_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[]));
|
||||
|
||||
CREATE POLICY see_extra_perms_user_select ON kafka_trigger FOR SELECT TO windmill_user
|
||||
USING (extra_perms ? CONCAT('u/', current_setting('session.user')));
|
||||
CREATE POLICY see_extra_perms_user_insert ON kafka_trigger FOR INSERT TO windmill_user
|
||||
WITH CHECK ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
|
||||
CREATE POLICY see_extra_perms_user_update ON kafka_trigger FOR UPDATE TO windmill_user
|
||||
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
|
||||
CREATE POLICY see_extra_perms_user_delete ON kafka_trigger FOR DELETE TO windmill_user
|
||||
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
|
||||
|
||||
CREATE POLICY see_extra_perms_groups_select ON kafka_trigger FOR SELECT TO windmill_user
|
||||
USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]);
|
||||
CREATE POLICY see_extra_perms_groups_insert ON kafka_trigger FOR INSERT TO windmill_user
|
||||
WITH CHECK (exists(
|
||||
SELECT key, value FROM jsonb_each_text(extra_perms)
|
||||
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
|
||||
AND value::boolean));
|
||||
CREATE POLICY see_extra_perms_groups_update ON kafka_trigger FOR UPDATE TO windmill_user
|
||||
USING (exists(
|
||||
SELECT key, value FROM jsonb_each_text(extra_perms)
|
||||
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
|
||||
AND value::boolean));
|
||||
CREATE POLICY see_extra_perms_groups_delete ON kafka_trigger FOR DELETE TO windmill_user
|
||||
USING (exists(
|
||||
SELECT key, value FROM jsonb_each_text(extra_perms)
|
||||
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
|
||||
AND value::boolean));
|
||||
@@ -171,6 +171,5 @@
|
||||
"user-library-modify",
|
||||
"user-library-read"
|
||||
]
|
||||
},
|
||||
"snowflake_oauth": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,6 @@ static PYTHON_IMPORTS_REPLACEMENT: phf::Map<&'static str, &'static str> = phf_ma
|
||||
"lokalise" => "python-lokalise-api",
|
||||
"msgraph" => "msgraph-sdk",
|
||||
"pythonjsonlogger" => "python-json-logger",
|
||||
"socks" => "PySocks",
|
||||
};
|
||||
|
||||
fn replace_import(x: String) -> String {
|
||||
|
||||
@@ -198,7 +198,6 @@ pub enum ResourceOrVariablePath {
|
||||
pub struct FileResource {
|
||||
pub resource_path: ResourceOrVariablePath,
|
||||
pub target_path: String,
|
||||
pub mode: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -438,65 +437,36 @@ fn count_consecutive_vs(s: &str) -> usize {
|
||||
|
||||
fn parse_file_resource(yaml: &Yaml) -> anyhow::Result<FileResource> {
|
||||
if let Yaml::Hash(f) = yaml {
|
||||
let target_path = f
|
||||
.get(&Yaml::String("target".to_string()))
|
||||
.and_then(|x| x.as_str())
|
||||
.map(|x| x.to_string())
|
||||
.ok_or(anyhow!(
|
||||
"No `target` provided for the file. Please input a target path (only relative paths are allowed) where the ansible playbook can read this file.",
|
||||
))?;
|
||||
|
||||
let mut mode = None;
|
||||
if let Some(u) = f.get(&Yaml::String("mode".to_string())) {
|
||||
let mode_val: u32 = match u {
|
||||
Yaml::Integer(u) => {
|
||||
u.clone().try_into().map_err(|e| {
|
||||
anyhow!(
|
||||
"Invalid value for `mode` permissions property on targeted file to: {}, err: {e}",
|
||||
target_path
|
||||
)
|
||||
})?
|
||||
}
|
||||
Yaml::String(s) => {
|
||||
let val = if s.starts_with("0b") {
|
||||
u32::from_str_radix(&s[2..], 2)
|
||||
} else if s.starts_with("0o") {
|
||||
u32::from_str_radix(&s[2..], 8)
|
||||
} else if s.starts_with("0") {
|
||||
u32::from_str_radix(&s[1..], 8)
|
||||
} else {
|
||||
u32::from_str_radix(s, 8)
|
||||
};
|
||||
|
||||
val.map_err(|e| anyhow!("Error parsing permission mode value {s}: {e}"))?
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!("Invalid field in `mode`, expected integer like 0o644"));
|
||||
}
|
||||
};
|
||||
if mode_val <= 0o777 {
|
||||
mode = Some(mode_val);
|
||||
} else {
|
||||
return Err(anyhow!("The provided value for `mode` is too big. Make sure that you are using the octal prefix (0o), e.g. `mode: 0o644`"));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(Yaml::String(resource_path)) = f.get(&Yaml::String("resource".to_string())) {
|
||||
let target_path = f
|
||||
.get(&Yaml::String("target".to_string()))
|
||||
.and_then(|x| x.as_str())
|
||||
.map(|x| x.to_string())
|
||||
.ok_or(anyhow!(
|
||||
"No `target` provided for file resource {}. Please input a target relative path for the ansible playbook to see this file.",
|
||||
resource_path
|
||||
))?;
|
||||
return Ok(FileResource {
|
||||
resource_path: ResourceOrVariablePath::Resource(resource_path.clone()),
|
||||
target_path,
|
||||
mode,
|
||||
});
|
||||
}
|
||||
if let Some(Yaml::String(resource_path)) = f.get(&Yaml::String("variable".to_string())) {
|
||||
let target_path = f
|
||||
.get(&Yaml::String("target".to_string()))
|
||||
.and_then(|x| x.as_str())
|
||||
.map(|x| x.to_string())
|
||||
.ok_or(anyhow!(
|
||||
"No `target` provided for file resource {}. Please input a target relative path for the ansible playbook to see this file.",
|
||||
resource_path
|
||||
))?;
|
||||
return Ok(FileResource {
|
||||
resource_path: ResourceOrVariablePath::Variable(resource_path.clone()),
|
||||
target_path,
|
||||
mode,
|
||||
});
|
||||
}
|
||||
return Err(anyhow!(
|
||||
"Files should have a `resource` or `variable` field that will be the contents of the local text file"
|
||||
"File resource should have a `resource` field, linking to a text file resource"
|
||||
));
|
||||
}
|
||||
return Err(anyhow!("Invalid file resource: Should be a dictionary."));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
use anyhow::Context;
|
||||
use git_version::git_version;
|
||||
use monitor::{
|
||||
reload_timeout_wait_result_setting, send_current_log_file_to_object_store,
|
||||
send_logs_to_object_store,
|
||||
@@ -30,19 +31,18 @@ use windmill_common::ee::{maybe_renew_license_key_on_start, LICENSE_KEY_ID, LICE
|
||||
|
||||
use windmill_common::{
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING,
|
||||
CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
DEFAULT_TAGS_WORKSPACES_SETTING, ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING,
|
||||
EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_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, SMTP_SETTING,
|
||||
TIMEOUT_WAIT_RESULT_SETTING,
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ERROR_CHANNELS_SETTING,
|
||||
CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
|
||||
EXTRA_PIP_INDEX_URL_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, SMTP_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
},
|
||||
scripts::ScriptLang,
|
||||
stats_ee::schedule_stats,
|
||||
utils::{hostname, rd_string, Mode, GIT_VERSION},
|
||||
utils::{hostname, rd_string, Mode},
|
||||
worker::{reload_custom_tags_setting, HUB_CACHE_DIR, TMP_DIR, WORKER_GROUP},
|
||||
DB, METRICS_ENABLED,
|
||||
};
|
||||
@@ -67,17 +67,16 @@ use windmill_worker::{
|
||||
get_hub_script_content_and_requirements, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR,
|
||||
BUN_DEPSTAR_CACHE_DIR, DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM,
|
||||
GO_BIN_CACHE_DIR, GO_CACHE_DIR, LOCK_CACHE_DIR, PIP_CACHE_DIR, POWERSHELL_CACHE_DIR,
|
||||
PY311_CACHE_DIR, RUST_CACHE_DIR, TAR_PIP_CACHE_DIR, TMP_LOGS_DIR, UV_CACHE_DIR,
|
||||
RUST_CACHE_DIR, TAR_PIP_CACHE_DIR, TMP_LOGS_DIR, UV_CACHE_DIR,
|
||||
};
|
||||
|
||||
use crate::monitor::{
|
||||
initial_load, load_keep_job_dir, load_metrics_debug_enabled, load_require_preexisting_user,
|
||||
load_tag_per_workspace_enabled, load_tag_per_workspace_workspaces, monitor_db, monitor_pool,
|
||||
reload_base_url_setting, reload_bunfig_install_scopes_setting,
|
||||
reload_critical_alert_mute_ui_setting, reload_critical_error_channels_setting,
|
||||
reload_extra_pip_index_url_setting, reload_hub_base_url_setting,
|
||||
reload_job_default_timeout_setting, reload_jwt_secret_setting, reload_license_key,
|
||||
reload_npm_config_registry_setting, reload_pip_index_url_setting,
|
||||
reload_critical_error_channels_setting, reload_extra_pip_index_url_setting,
|
||||
reload_hub_base_url_setting, 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_smtp_config,
|
||||
reload_worker_config,
|
||||
};
|
||||
@@ -85,6 +84,7 @@ use crate::monitor::{
|
||||
#[cfg(feature = "parquet")]
|
||||
use crate::monitor::reload_s3_cache_setting;
|
||||
|
||||
const GIT_VERSION: &str = git_version!(args = ["--tag", "--always"], fallback = "unknown-version");
|
||||
const DEFAULT_NUM_WORKERS: usize = 1;
|
||||
const DEFAULT_PORT: u16 = 8000;
|
||||
const DEFAULT_SERVER_BIND_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
|
||||
@@ -531,7 +531,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
|
||||
#[cfg(feature = "tantivy")]
|
||||
let (index_reader, index_writer) = if should_index_jobs {
|
||||
let (r, w) = windmill_indexer::completed_runs_ee::init_index(&db).await?;
|
||||
let (r, w) = windmill_indexer::indexer_ee::init_index(&db).await?;
|
||||
(Some(r), Some(w))
|
||||
} else {
|
||||
(None, None)
|
||||
@@ -543,61 +543,26 @@ Windmill Community Edition {GIT_VERSION}
|
||||
let index_writer2 = index_writer.clone();
|
||||
async {
|
||||
if let Some(index_writer) = index_writer2 {
|
||||
windmill_indexer::completed_runs_ee::run_indexer(
|
||||
db.clone(),
|
||||
index_writer,
|
||||
indexer_rx,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "tantivy", feature = "parquet"))]
|
||||
let (log_index_reader, log_index_writer) = if should_index_jobs {
|
||||
let (r, w) = windmill_indexer::service_logs_ee::init_index(&db).await?;
|
||||
(Some(r), Some(w))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "tantivy", feature = "parquet"))]
|
||||
let log_indexer_f = {
|
||||
let log_indexer_rx = killpill_rx.resubscribe();
|
||||
let log_index_writer2 = log_index_writer.clone();
|
||||
async {
|
||||
if let Some(log_index_writer) = log_index_writer2 {
|
||||
windmill_indexer::service_logs_ee::run_indexer(
|
||||
db.clone(),
|
||||
log_index_writer,
|
||||
log_indexer_rx,
|
||||
)
|
||||
.await;
|
||||
windmill_indexer::indexer_ee::run_indexer(db.clone(), index_writer, indexer_rx)
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "tantivy"))]
|
||||
let index_reader = None;
|
||||
let (index_reader, index_writer) = (None, None);
|
||||
|
||||
#[cfg(not(feature = "tantivy"))]
|
||||
let indexer_f = async { Ok(()) as anyhow::Result<()> };
|
||||
|
||||
#[cfg(not(all(feature = "tantivy", feature = "parquet")))]
|
||||
let log_index_reader = None;
|
||||
|
||||
#[cfg(not(all(feature = "tantivy", feature = "parquet")))]
|
||||
let log_indexer_f = async { Ok(()) as anyhow::Result<()> };
|
||||
|
||||
let server_f = async {
|
||||
if !is_agent {
|
||||
windmill_api::run_server(
|
||||
db.clone(),
|
||||
rsmq2,
|
||||
index_reader,
|
||||
log_index_reader,
|
||||
index_writer,
|
||||
addr,
|
||||
server_killpill_rx,
|
||||
base_internal_tx,
|
||||
@@ -817,12 +782,6 @@ Windmill Community Edition {GIT_VERSION}
|
||||
tracing::error!(error = %e, "Could not reload jwt secret setting");
|
||||
}
|
||||
},
|
||||
CRITICAL_ALERT_MUTE_UI_SETTING => {
|
||||
tracing::info!("Critical alert UI setting changed");
|
||||
if let Err(e) = reload_critical_alert_mute_ui_setting(&db).await {
|
||||
tracing::error!(error = %e, "Could not reload critical alert UI setting");
|
||||
}
|
||||
},
|
||||
a @_ => {
|
||||
tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a);
|
||||
}
|
||||
@@ -883,8 +842,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
monitor_f,
|
||||
server_f,
|
||||
metrics_f,
|
||||
indexer_f,
|
||||
log_indexer_f
|
||||
indexer_f
|
||||
)?;
|
||||
} else {
|
||||
tracing::info!("Nothing to do, exiting.");
|
||||
@@ -997,14 +955,13 @@ pub async fn run_workers<R: rsmq_async::RsmqConnection + Send + Sync + Clone + '
|
||||
for x in [
|
||||
LOCK_CACHE_DIR,
|
||||
TMP_LOGS_DIR,
|
||||
PIP_CACHE_DIR,
|
||||
UV_CACHE_DIR,
|
||||
TAR_PIP_CACHE_DIR,
|
||||
DENO_CACHE_DIR,
|
||||
DENO_CACHE_DIR_DEPS,
|
||||
DENO_CACHE_DIR_NPM,
|
||||
BUN_CACHE_DIR,
|
||||
PY311_CACHE_DIR,
|
||||
PIP_CACHE_DIR,
|
||||
BUN_DEPSTAR_CACHE_DIR,
|
||||
BUN_BUNDLE_CACHE_DIR,
|
||||
GO_CACHE_DIR,
|
||||
|
||||
@@ -19,6 +19,7 @@ use tokio::{
|
||||
sync::{mpsc, RwLock},
|
||||
};
|
||||
|
||||
use uuid::Uuid;
|
||||
#[cfg(feature = "embedding")]
|
||||
use windmill_api::embeddings::update_embeddings_db;
|
||||
use windmill_api::{
|
||||
@@ -32,14 +33,14 @@ use windmill_common::{
|
||||
auth::JWT_SECRET,
|
||||
ee::CriticalErrorChannel,
|
||||
error,
|
||||
flow_status::FlowStatusModule,
|
||||
flow_status::{FlowStatusModule, ParsedFlowStatusGetter as _},
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING,
|
||||
CRITICAL_ERROR_CHANNELS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
DEFAULT_TAGS_WORKSPACES_SETTING, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
|
||||
EXTRA_PIP_INDEX_URL_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,
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ERROR_CHANNELS_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_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, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
},
|
||||
@@ -51,11 +52,11 @@ use windmill_common::{
|
||||
utils::{now_from_db, rd_string, report_critical_error, Mode},
|
||||
worker::{
|
||||
load_worker_config, make_pull_query, make_suspended_pull_query, reload_custom_tags_setting,
|
||||
update_min_version, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, SMTP_CONFIG,
|
||||
WORKER_CONFIG, WORKER_GROUP,
|
||||
DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, SMTP_CONFIG, WORKER_CONFIG,
|
||||
WORKER_GROUP,
|
||||
},
|
||||
BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL,
|
||||
HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED,
|
||||
BASE_URL, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS,
|
||||
METRICS_DEBUG_ENABLED, METRICS_ENABLED,
|
||||
};
|
||||
use windmill_queue::cancel_job;
|
||||
use windmill_worker::{
|
||||
@@ -111,9 +112,6 @@ lazy_static::lazy_static! {
|
||||
"Number of jobs in the queue",
|
||||
&["tag"]
|
||||
).unwrap();
|
||||
|
||||
static ref QUEUE_COUNT_TAGS: Arc<RwLock<Vec<String>>> = Arc::new(RwLock::new(Vec::new()));
|
||||
|
||||
}
|
||||
|
||||
pub async fn initial_load(
|
||||
@@ -131,10 +129,6 @@ pub async fn initial_load(
|
||||
tracing::error!("Error loading expose debug metrics: {e:#}");
|
||||
}
|
||||
|
||||
if let Err(e) = reload_critical_alert_mute_ui_setting(db).await {
|
||||
tracing::error!("Error loading critical alert mute ui setting: {e:#}");
|
||||
}
|
||||
|
||||
if let Err(e) = load_tag_per_workspace_enabled(db).await {
|
||||
tracing::error!("Error loading default tag per workpsace: {e:#}");
|
||||
}
|
||||
@@ -230,24 +224,6 @@ pub async fn load_tag_per_workspace_workspaces(db: &DB) -> error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reload_critical_alert_mute_ui_setting(db: &DB) -> error::Result<()> {
|
||||
if let Ok(Some(serde_json::Value::Bool(t))) =
|
||||
load_value_from_global_settings(db, CRITICAL_ALERT_MUTE_UI_SETTING).await
|
||||
{
|
||||
CRITICAL_ALERT_MUTE_UI_ENABLED.store(t, Ordering::Relaxed);
|
||||
|
||||
if t {
|
||||
if let Err(e) = sqlx::query!("UPDATE alerts SET acknowledged = true")
|
||||
.execute(db)
|
||||
.await
|
||||
{
|
||||
tracing::error!("Error updating alerts: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> {
|
||||
let metrics_enabled = load_value_from_global_settings(db, EXPOSE_DEBUG_METRICS_SETTING).await;
|
||||
match metrics_enabled {
|
||||
@@ -1087,10 +1063,6 @@ pub async fn monitor_db(
|
||||
}
|
||||
};
|
||||
|
||||
let update_min_worker_version_f = async {
|
||||
update_min_version(db).await;
|
||||
};
|
||||
|
||||
join!(
|
||||
expired_items_f,
|
||||
zombie_jobs_f,
|
||||
@@ -1099,7 +1071,6 @@ pub async fn monitor_db(
|
||||
worker_groups_alerts_f,
|
||||
jobs_waiting_alerts_f,
|
||||
apply_autoscaling_f,
|
||||
update_min_worker_version_f,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1119,23 +1090,12 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
|
||||
if metrics_enabled || save_metrics {
|
||||
let queue_counts = windmill_common::queue::get_queue_counts(db).await;
|
||||
|
||||
if metrics_enabled {
|
||||
for q in QUEUE_COUNT_TAGS.read().await.iter() {
|
||||
if queue_counts.get(q).is_none() {
|
||||
(*QUEUE_COUNT).with_label_values(&[q]).set(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut tags_to_watch = vec![];
|
||||
for q in queue_counts {
|
||||
let count = q.1;
|
||||
let tag = q.0;
|
||||
|
||||
if metrics_enabled {
|
||||
let metric = (*QUEUE_COUNT).with_label_values(&[&tag]);
|
||||
metric.set(count as i64);
|
||||
tags_to_watch.push(tag.to_string());
|
||||
}
|
||||
|
||||
// save queue_count and delay metrics per tag
|
||||
@@ -1160,10 +1120,6 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if metrics_enabled {
|
||||
let mut w = QUEUE_COUNT_TAGS.write().await;
|
||||
*w = tags_to_watch;
|
||||
}
|
||||
}
|
||||
|
||||
// clean queue metrics older than 14 days
|
||||
@@ -1330,7 +1286,7 @@ async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
ON CONFLICT (job_id) DO UPDATE SET logs = job_logs.logs || '\nRestarted job after not receiving job''s ping for too long the ' || now() || '\n\n' WHERE job_logs.job_id = $1", r.id)
|
||||
.execute(db).await;
|
||||
tracing::error!(error_message);
|
||||
report_critical_error(error_message, db.clone(), Some(&r.workspace_id), None).await;
|
||||
report_critical_error(error_message, db.clone()).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1444,7 +1400,7 @@ async fn handle_zombie_flows(
|
||||
flow.id, flow.workspace_id
|
||||
);
|
||||
tracing::error!(error_message);
|
||||
report_critical_error(error_message, db.clone(), Some(&flow.workspace_id), None).await;
|
||||
report_critical_error(error_message, db.clone()).await;
|
||||
// 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 AND canceled = false",
|
||||
@@ -1464,8 +1420,8 @@ async fn handle_zombie_flows(
|
||||
format!("Flow {id} was cancelled because it")
|
||||
}
|
||||
);
|
||||
report_critical_error(reason.clone(), db.clone(), Some(&flow.workspace_id), None).await;
|
||||
cancel_zombie_flow_job(db, flow, &rsmq, reason).await?;
|
||||
report_critical_error(reason.clone(), db.clone()).await;
|
||||
cancel_zombie_flow_job(db, &flow.id, &flow.workspace_id, &rsmq, reason).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1481,11 +1437,17 @@ async fn handle_zombie_flows(
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
|
||||
#[derive(sqlx::FromRow, Debug)]
|
||||
struct InQueueJobResult {
|
||||
id: uuid::Uuid,
|
||||
workspace_id: String,
|
||||
}
|
||||
|
||||
for flow in flows2 {
|
||||
let in_queue = sqlx::query_as::<_, QueuedJob>(
|
||||
"SELECT * FROM queue WHERE id = $1 AND running = true AND canceled = false",
|
||||
let in_queue = sqlx::query_as!(InQueueJobResult,
|
||||
"SELECT id, workspace_id FROM queue WHERE id = $1 AND running = true AND canceled = false",
|
||||
flow.parent_flow_id
|
||||
)
|
||||
.bind(flow.parent_flow_id)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
if let Some(job) = in_queue {
|
||||
@@ -1495,7 +1457,7 @@ async fn handle_zombie_flows(
|
||||
job.workspace_id,
|
||||
flow.last_ping
|
||||
);
|
||||
cancel_zombie_flow_job(db, job, &rsmq,
|
||||
cancel_zombie_flow_job(db, &job.id, &job.workspace_id, &rsmq,
|
||||
format!("Flow {} cancelled as one of the parallel branch {} was unable to make the last transition ", flow.parent_flow_id, flow.job_id))
|
||||
.await?;
|
||||
} else {
|
||||
@@ -1507,21 +1469,22 @@ async fn handle_zombie_flows(
|
||||
|
||||
async fn cancel_zombie_flow_job(
|
||||
db: &Pool<Postgres>,
|
||||
flow: QueuedJob,
|
||||
job_id: &Uuid,
|
||||
workspace_id: &str,
|
||||
rsmq: &Option<MultiplexedRsmq>,
|
||||
message: String,
|
||||
) -> Result<(), error::Error> {
|
||||
let tx = db.begin().await.unwrap();
|
||||
tracing::error!(
|
||||
"zombie flow detected: {} in workspace {}. Cancelling it.",
|
||||
flow.id,
|
||||
flow.workspace_id
|
||||
job_id,
|
||||
workspace_id
|
||||
);
|
||||
let (ntx, _) = cancel_job(
|
||||
"monitor",
|
||||
Some(message),
|
||||
flow.id,
|
||||
flow.workspace_id.as_str(),
|
||||
*job_id,
|
||||
workspace_id,
|
||||
tx,
|
||||
db,
|
||||
rsmq.clone(),
|
||||
|
||||
@@ -1112,7 +1112,6 @@ async fn test_deno_flow(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
is_trigger: None,
|
||||
}
|
||||
.into(),
|
||||
stop_after_if: Default::default(),
|
||||
@@ -1154,7 +1153,6 @@ async fn test_deno_flow(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
is_trigger: None,
|
||||
}
|
||||
.into(),
|
||||
stop_after_if: Default::default(),
|
||||
@@ -1278,8 +1276,6 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
is_trigger: None,
|
||||
|
||||
}.into(),
|
||||
stop_after_if: Default::default(),
|
||||
stop_after_all_iters_if: Default::default(),
|
||||
@@ -1331,7 +1327,6 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
is_trigger: None,
|
||||
}.into(),
|
||||
stop_after_if: Default::default(),
|
||||
stop_after_all_iters_if: Default::default(),
|
||||
@@ -1369,8 +1364,6 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
is_trigger: None,
|
||||
|
||||
}.into(),
|
||||
stop_after_if: Default::default(),
|
||||
stop_after_all_iters_if: Default::default(),
|
||||
@@ -1431,7 +1424,6 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
is_trigger: None,
|
||||
}.into(),
|
||||
stop_after_if: Default::default(),
|
||||
stop_after_all_iters_if: Default::default(),
|
||||
|
||||
@@ -19,7 +19,6 @@ parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/par
|
||||
prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus"]
|
||||
openidconnect = ["dep:openidconnect"]
|
||||
tantivy = ["dep:windmill-indexer"]
|
||||
kafka = ["dep:rdkafka"]
|
||||
|
||||
[dependencies]
|
||||
windmill-queue.workspace = true
|
||||
@@ -97,7 +96,6 @@ url = { workspace = true, optional = true}
|
||||
jsonwebtoken = { workspace = true }
|
||||
matchit.workspace = true
|
||||
tokio-tungstenite.workspace = true
|
||||
rdkafka = { workspace = true, optional = true }
|
||||
|
||||
pin-project.workspace = true
|
||||
http.workspace = true
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,507 +0,0 @@
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
variables::decrypt,
|
||||
};
|
||||
use anthropic::AnthropicCache;
|
||||
use axum::{
|
||||
body::Bytes,
|
||||
extract::{Path, Query},
|
||||
response::IntoResponse,
|
||||
routing::post,
|
||||
Extension, Router,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use mistral::MistralCache;
|
||||
use openai::OpenaiCache;
|
||||
use quick_cache::sync::Cache;
|
||||
use reqwest::{Client, RequestBuilder};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use windmill_audit::audit_ee::audit_log;
|
||||
use windmill_audit::ActionKind;
|
||||
use windmill_common::error::{to_anyhow, Result};
|
||||
use windmill_common::variables::build_crypt;
|
||||
|
||||
use windmill_common::error::Error;
|
||||
|
||||
use serde_json::value::{RawValue, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new()
|
||||
.timeout(std::time::Duration::from_secs(60 * 5))
|
||||
.user_agent("windmill/beta")
|
||||
.build().unwrap();
|
||||
}
|
||||
|
||||
trait AiRequest {
|
||||
fn prepare_request(self, path: &str, body: Bytes) -> Result<RequestBuilder>;
|
||||
}
|
||||
|
||||
mod openai {
|
||||
use super::*;
|
||||
|
||||
use super::{get_variable_or_self, KeyCache};
|
||||
|
||||
const API_VERSION: &str = "2023-05-15";
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct OpenaiResource {
|
||||
api_key: String,
|
||||
organization_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct OpenaiClientCredentialsOauthResource {
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
token_url: String,
|
||||
user: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(untagged, rename_all = "snake_case")]
|
||||
enum OpenaiConfig {
|
||||
Resource(OpenaiResource),
|
||||
ClientCredentialsOauthResource(OpenaiClientCredentialsOauthResource),
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref OPENAI_AZURE_BASE_PATH: Option<String> = std::env::var("OPENAI_AZURE_BASE_PATH").ok();
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct OpenaiCredentials {
|
||||
access_token: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct OpenaiCache {
|
||||
api_key: String,
|
||||
organization_id: Option<String>,
|
||||
azure_base_path: Option<String>,
|
||||
user: Option<String>,
|
||||
}
|
||||
|
||||
impl OpenaiCache {
|
||||
pub fn new(
|
||||
api_key: String,
|
||||
organization_id: Option<String>,
|
||||
azure_base_path: Option<String>,
|
||||
user: Option<String>,
|
||||
) -> Self {
|
||||
Self { api_key, organization_id, azure_base_path, user }
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_URL: &str = "https://api.openai.com/v1";
|
||||
impl AiRequest for OpenaiCache {
|
||||
fn prepare_request(self, openai_path: &str, mut body: Bytes) -> Result<RequestBuilder> {
|
||||
let OpenaiCache { api_key, azure_base_path, organization_id, user } = self;
|
||||
if user.is_some() {
|
||||
tracing::debug!("Adding user to request body");
|
||||
let mut json_body: HashMap<String, Box<RawValue>> = serde_json::from_slice(&body)
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!("Failed to parse request body: {}", e))
|
||||
})?;
|
||||
|
||||
let user_json_string = serde_json::Value::String(user.unwrap()).to_string(); // makes sure to escape characters
|
||||
|
||||
json_body.insert(
|
||||
"user".to_string(),
|
||||
RawValue::from_string(user_json_string)
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to parse user: {}", e)))?,
|
||||
);
|
||||
|
||||
body = serde_json::to_vec(&json_body)
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!("Failed to reserialize request body: {}", e))
|
||||
})?
|
||||
.into();
|
||||
}
|
||||
|
||||
let base_url = if let Some(base_url) = azure_base_path {
|
||||
base_url
|
||||
} else {
|
||||
BASE_URL.to_string()
|
||||
};
|
||||
let url = format!("{}/{}", base_url, openai_path);
|
||||
let mut request = HTTP_CLIENT
|
||||
.post(url)
|
||||
.header("content-type", "application/json")
|
||||
.body(body);
|
||||
|
||||
if base_url != BASE_URL {
|
||||
request = request
|
||||
.header("api-key", api_key)
|
||||
.query(&[("api-version", API_VERSION)])
|
||||
} else {
|
||||
request = request.header("authorization", format!("Bearer {}", api_key))
|
||||
}
|
||||
|
||||
if let Some(org_id) = organization_id {
|
||||
request = request.header("OpenAI-Organization", org_id);
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_openai_key_using_credentials_flow(
|
||||
mut resource: OpenaiClientCredentialsOauthResource,
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
) -> Result<String> {
|
||||
resource.client_id = get_variable_or_self(resource.client_id, db, w_id).await?;
|
||||
resource.client_secret = get_variable_or_self(resource.client_secret, db, w_id).await?;
|
||||
resource.token_url = get_variable_or_self(resource.token_url, db, w_id).await?;
|
||||
let mut params = HashMap::new();
|
||||
params.insert("grant_type", "client_credentials");
|
||||
let response = HTTP_CLIENT
|
||||
.post(resource.token_url)
|
||||
.form(¶ms)
|
||||
.basic_auth(resource.client_id, Some(resource.client_secret))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
Error::InternalErr(format!(
|
||||
"Failed to get OpenAI credentials using credentials flow: {}",
|
||||
err
|
||||
))
|
||||
})?;
|
||||
let response = response.json::<OpenaiCredentials>().await.map_err(|err| {
|
||||
Error::InternalErr(format!(
|
||||
"Failed to parse OpenAI credentials from credentials flow: {}",
|
||||
err
|
||||
))
|
||||
})?;
|
||||
Ok(response.access_token)
|
||||
}
|
||||
|
||||
pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result<KeyCache> {
|
||||
let config = serde_json::from_value(resource)
|
||||
.map_err(|e| Error::InternalErr(format!("validating openai resource {e:#}")))?;
|
||||
|
||||
let mut user = None::<String>;
|
||||
let mut resource = match config {
|
||||
OpenaiConfig::Resource(resource) => {
|
||||
tracing::debug!("Getting OpenAI key from static resource");
|
||||
resource
|
||||
}
|
||||
OpenaiConfig::ClientCredentialsOauthResource(resource) => {
|
||||
tracing::debug!("Getting OpenAI key with client credentials flow");
|
||||
user = resource.user.clone();
|
||||
let token = get_openai_key_using_credentials_flow(resource, db, w_id).await?;
|
||||
OpenaiResource { api_key: token, organization_id: None }
|
||||
}
|
||||
};
|
||||
|
||||
resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?;
|
||||
|
||||
if let Some(organization_id) = resource.organization_id {
|
||||
resource.organization_id = Some(get_variable_or_self(organization_id, db, w_id).await?);
|
||||
}
|
||||
|
||||
if user.is_some() {
|
||||
user = Some(get_variable_or_self(user.unwrap(), db, w_id).await?);
|
||||
}
|
||||
|
||||
let azure_base_path = sqlx::query_scalar!(
|
||||
"SELECT value
|
||||
FROM global_settings
|
||||
WHERE name = 'openai_azure_base_path'",
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
|
||||
let azure_base_path = if let Some(azure_base_path) = azure_base_path {
|
||||
Some(
|
||||
serde_json::from_value::<String>(azure_base_path).map_err(|e| {
|
||||
Error::InternalErr(format!("validating openai azure base path {e:#}"))
|
||||
})?,
|
||||
)
|
||||
} else {
|
||||
OPENAI_AZURE_BASE_PATH.clone()
|
||||
};
|
||||
|
||||
let workspace_cache = OpenaiCache::new(
|
||||
resource.api_key.clone(),
|
||||
resource.organization_id.clone(),
|
||||
azure_base_path.clone(),
|
||||
user.clone(),
|
||||
);
|
||||
Ok(KeyCache::Openai(workspace_cache))
|
||||
}
|
||||
}
|
||||
|
||||
mod anthropic {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Deserialize, Debug)]
|
||||
pub struct AnthropicCache {
|
||||
#[serde(rename = "apiKey")]
|
||||
pub api_key: String,
|
||||
}
|
||||
|
||||
const API_VERSION: &str = "2023-06-01";
|
||||
|
||||
impl AnthropicCache {
|
||||
pub fn new(api_key: String) -> Self {
|
||||
Self { api_key }
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_URL: &str = "https://api.anthropic.com";
|
||||
impl AiRequest for AnthropicCache {
|
||||
fn prepare_request(self, anthropic_path: &str, body: Bytes) -> Result<RequestBuilder> {
|
||||
let AnthropicCache { api_key } = self;
|
||||
let url = format!("{}/{}", BASE_URL, anthropic_path);
|
||||
let request = HTTP_CLIENT
|
||||
.post(url)
|
||||
.header("x-api-key", api_key)
|
||||
.header("anthropic-version", API_VERSION)
|
||||
.header("content-type", "application/json")
|
||||
.body(body);
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result<KeyCache> {
|
||||
let mut resource: AnthropicCache = serde_json::from_value(resource)
|
||||
.map_err(|e| Error::InternalErr(format!("validating anthropic resource {e:#}")))?;
|
||||
resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?;
|
||||
let workspace_cache = AnthropicCache::new(resource.api_key);
|
||||
Ok(KeyCache::Anthropic(workspace_cache))
|
||||
}
|
||||
}
|
||||
|
||||
mod mistral {
|
||||
use super::*;
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct MistralCache {
|
||||
#[serde(rename = "apiKey")]
|
||||
pub api_key: String,
|
||||
}
|
||||
|
||||
impl MistralCache {
|
||||
pub fn new(api_key: String) -> Self {
|
||||
Self { api_key }
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_URL: &str = "https://api.mistral.ai";
|
||||
impl AiRequest for MistralCache {
|
||||
fn prepare_request(self, mistral_path: &str, body: Bytes) -> Result<RequestBuilder> {
|
||||
let MistralCache { api_key } = self;
|
||||
|
||||
let url = format!("{}/{}", BASE_URL, mistral_path);
|
||||
let request = HTTP_CLIENT
|
||||
.post(url)
|
||||
.header("content-type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.header("authorization", format!("Bearer {}", api_key))
|
||||
.body(body);
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result<KeyCache> {
|
||||
let mut resource: MistralCache = serde_json::from_value(resource)
|
||||
.map_err(|e| Error::InternalErr(format!("validating mistral resource {e:#}")))?;
|
||||
resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?;
|
||||
|
||||
let workspace_cache = MistralCache::new(resource.api_key);
|
||||
Ok(KeyCache::Mistral(workspace_cache))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum KeyCache {
|
||||
Openai(OpenaiCache),
|
||||
Anthropic(AnthropicCache),
|
||||
Mistral(MistralCache),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AiCache {
|
||||
pub path: String,
|
||||
pub cached_key: KeyCache,
|
||||
pub expires_at: std::time::Instant,
|
||||
}
|
||||
|
||||
impl AiCache {
|
||||
pub fn new(path: String, cached_key: KeyCache) -> Self {
|
||||
Self {
|
||||
path,
|
||||
cached_key,
|
||||
expires_at: std::time::Instant::now() + std::time::Duration::from_secs(60),
|
||||
}
|
||||
}
|
||||
fn is_expired(&self) -> bool {
|
||||
self.expires_at < std::time::Instant::now()
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref AI_KEY_CACHE: Cache<String, AiCache> = Cache::new(500);
|
||||
}
|
||||
|
||||
struct Variable {
|
||||
value: String,
|
||||
is_secret: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct ProxyQueryParams {
|
||||
no_cache: Option<bool>,
|
||||
}
|
||||
|
||||
async fn get_variable_or_self(path: String, db: &DB, w_id: &str) -> Result<String> {
|
||||
if !path.starts_with("$var:") {
|
||||
return Ok(path);
|
||||
}
|
||||
let path = path.strip_prefix("$var:").unwrap().to_string();
|
||||
let mut variable = sqlx::query_as!(
|
||||
Variable,
|
||||
"SELECT value, is_secret
|
||||
FROM variable
|
||||
WHERE path = $1 AND workspace_id = $2",
|
||||
&path,
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
if variable.is_secret {
|
||||
let mc = build_crypt(db, w_id).await?;
|
||||
variable.value = decrypt(&mc, variable.value)?;
|
||||
}
|
||||
Ok(variable.value)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct AiResource {
|
||||
pub path: String,
|
||||
#[serde(deserialize_with = "check_if_valid_ai_provider")]
|
||||
pub provider: String,
|
||||
}
|
||||
|
||||
fn check_if_valid_ai_provider<'de, D>(provider: D) -> std::result::Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let provider = String::deserialize(provider)?;
|
||||
match provider.as_str() {
|
||||
"anthropic" | "openai" | "mistral" => Ok(provider),
|
||||
_ => Err(serde::de::Error::custom(
|
||||
"Only the following Ai providers are supported: openai, anthropic and mistral"
|
||||
.to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
let router = Router::new().route("/proxy/*ai", post(proxy));
|
||||
|
||||
router
|
||||
}
|
||||
|
||||
async fn proxy(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, ai_path)): Path<(String, String)>,
|
||||
Query(query_params): Query<ProxyQueryParams>,
|
||||
body: Bytes,
|
||||
) -> impl IntoResponse {
|
||||
let workspace_cache = AI_KEY_CACHE.get(&w_id);
|
||||
let ai_cache = match workspace_cache {
|
||||
Some(cache) if !cache.is_expired() && !query_params.no_cache.unwrap_or(false) => {
|
||||
cache.cached_key
|
||||
}
|
||||
_ => {
|
||||
let ai_resource = sqlx::query_scalar!(
|
||||
"SELECT ai_resource FROM workspace_settings WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?;
|
||||
|
||||
if ai_resource.is_none() {
|
||||
return Err(Error::InternalErr("AI resource not configured".to_string()));
|
||||
}
|
||||
|
||||
let ai_resource = serde_json::from_value::<AiResource>(ai_resource.unwrap())
|
||||
.map_err(|e| Error::BadRequest(e.to_string()))?;
|
||||
let ai_resource_path = ai_resource.path;
|
||||
|
||||
let resource = sqlx::query_scalar!(
|
||||
"SELECT value
|
||||
FROM resource
|
||||
WHERE path = $1 AND workspace_id = $2",
|
||||
&ai_resource_path,
|
||||
&w_id
|
||||
)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
Error::InternalErr(format!(
|
||||
"Could not find the {} resource at path {ai_resource_path}, update the resource path in the workspace settings", ai_resource.provider
|
||||
))
|
||||
})?;
|
||||
|
||||
if resource.is_none() {
|
||||
return Err(Error::InternalErr(format!(
|
||||
"{} resource missing value",
|
||||
ai_resource.provider
|
||||
)));
|
||||
}
|
||||
|
||||
let resource = resource.unwrap();
|
||||
|
||||
let ai_cache = match ai_resource.provider.as_str() {
|
||||
"openai" => openai::get_cached_value(&db, &w_id, resource).await,
|
||||
"anthropic" => anthropic::get_cached_value(&db, &w_id, resource).await,
|
||||
"mistral" => mistral::get_cached_value(&db, &w_id, resource).await,
|
||||
provider => {
|
||||
return Err(Error::BadRequest(format!("{} is not supported", provider)))
|
||||
}
|
||||
};
|
||||
let ai_cache = ai_cache?;
|
||||
AI_KEY_CACHE.insert(
|
||||
w_id.clone(),
|
||||
AiCache::new(ai_resource_path, ai_cache.clone()),
|
||||
);
|
||||
ai_cache
|
||||
}
|
||||
};
|
||||
let (path, request) = match ai_cache {
|
||||
KeyCache::Openai(cached) => ("openai_path", cached.prepare_request(&ai_path, body)),
|
||||
KeyCache::Anthropic(cached) => ("anthropic_path", cached.prepare_request(&ai_path, body)),
|
||||
KeyCache::Mistral(cached) => ("mistral_path", cached.prepare_request(&ai_path, body)),
|
||||
};
|
||||
|
||||
let response = request?.send().await.map_err(to_anyhow)?;
|
||||
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"ai.request",
|
||||
ActionKind::Execute,
|
||||
&w_id,
|
||||
Some(&authed.email),
|
||||
Some([(path, &format!("{:?}", ai_path)[..])].into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
if response.error_for_status_ref().is_err() {
|
||||
let err_msg = response.text().await.unwrap_or("".to_string());
|
||||
return Err(Error::AiError(err_msg));
|
||||
}
|
||||
|
||||
let status_code = response.status();
|
||||
let headers = response.headers().clone();
|
||||
let stream = response.bytes_stream();
|
||||
Ok((status_code, headers, axum::body::Body::from_stream(stream)))
|
||||
}
|
||||
@@ -7,7 +7,6 @@ use std::collections::HashMap;
|
||||
* Please see the included NOTICE for copyright information and
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
resources::get_resource_value_interpolated_internal,
|
||||
@@ -17,14 +16,6 @@ use crate::{
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
HTTP_CLIENT,
|
||||
};
|
||||
#[cfg(feature = "parquet")]
|
||||
use crate::{
|
||||
job_helpers_ee::{
|
||||
get_random_file_name, get_s3_resource, get_workspace_s3_resource, upload_file_internal,
|
||||
UploadFileResponse,
|
||||
},
|
||||
users::fetch_api_authed_from_permissioned_as,
|
||||
};
|
||||
use axum::{
|
||||
extract::{Extension, Json, Path, Query},
|
||||
response::IntoResponse,
|
||||
@@ -32,13 +23,7 @@ use axum::{
|
||||
Router,
|
||||
};
|
||||
use hyper::StatusCode;
|
||||
#[cfg(feature = "parquet")]
|
||||
use itertools::Itertools;
|
||||
use magic_crypt::MagicCryptTrait;
|
||||
#[cfg(feature = "parquet")]
|
||||
use object_store::{Attribute, Attributes};
|
||||
#[cfg(feature = "parquet")]
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, value::RawValue};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -47,8 +32,6 @@ use sqlx::{types::Uuid, FromRow};
|
||||
use std::str;
|
||||
use windmill_audit::audit_ee::audit_log;
|
||||
use windmill_audit::ActionKind;
|
||||
#[cfg(feature = "parquet")]
|
||||
use windmill_common::s3_helpers::build_object_store_client;
|
||||
use windmill_common::{
|
||||
apps::ListAppQuery,
|
||||
db::UserDB,
|
||||
@@ -86,7 +69,6 @@ pub fn workspaced_service() -> Router {
|
||||
pub fn unauthed_service() -> Router {
|
||||
Router::new()
|
||||
.route("/execute_component/*path", post(execute_component))
|
||||
.route("/upload_s3_file/*path", post(upload_s3_file_from_app))
|
||||
.route("/public_app/:secret", get(get_public_app_by_secret))
|
||||
.route("/public_resource/*path", get(get_public_resource))
|
||||
}
|
||||
@@ -197,14 +179,6 @@ pub struct PolicyTriggerableInputs {
|
||||
allow_user_resources: AllowUserResources,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct S3Input {
|
||||
allowed_resources: Vec<String>,
|
||||
allow_user_resources: bool,
|
||||
allow_workspace_resource: bool,
|
||||
file_key_regex: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Policy {
|
||||
pub on_behalf_of: Option<String>,
|
||||
@@ -218,7 +192,6 @@ pub struct Policy {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub triggerables_v2: Option<HashMap<String, PolicyTriggerableInputs>>,
|
||||
pub execution_mode: ExecutionMode,
|
||||
pub s3_inputs: Option<Vec<S3Input>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -459,7 +432,9 @@ async fn get_latest_version(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
|
||||
) -> JsonResult<Option<AppHistory>> {
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let row = sqlx::query!(
|
||||
"SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg
|
||||
@@ -482,6 +457,7 @@ async fn get_latest_version(
|
||||
} else {
|
||||
return Ok(Json(None));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async fn update_app_history(
|
||||
@@ -1091,49 +1067,6 @@ fn digest(code: &str) -> String {
|
||||
format!("rawscript/{:x}", result)
|
||||
}
|
||||
|
||||
async fn get_on_behalf_details_from_policy_and_authed(
|
||||
policy: &Policy,
|
||||
opt_authed: &Option<ApiAuthed>,
|
||||
) -> Result<(String, String, String)> {
|
||||
let (username, permissioned_as, email) = match policy.execution_mode {
|
||||
ExecutionMode::Anonymous => {
|
||||
let username = opt_authed
|
||||
.as_ref()
|
||||
.map(|a| a.username.clone())
|
||||
.unwrap_or_else(|| "anonymous".to_string());
|
||||
let (permissioned_as, email) = get_on_behalf_of(&policy)?;
|
||||
(username, permissioned_as, email)
|
||||
}
|
||||
ExecutionMode::Publisher => {
|
||||
let username = opt_authed
|
||||
.as_ref()
|
||||
.map(|a| a.username.clone())
|
||||
.ok_or_else(|| {
|
||||
Error::BadRequest(
|
||||
"publisher execution mode requires authentication".to_string(),
|
||||
)
|
||||
})?;
|
||||
let (permissioned_as, email) = get_on_behalf_of(&policy)?;
|
||||
(username, permissioned_as, email)
|
||||
}
|
||||
ExecutionMode::Viewer => {
|
||||
let (username, email) = opt_authed
|
||||
.as_ref()
|
||||
.map(|a| (a.username.clone(), a.email.clone()))
|
||||
.ok_or_else(|| {
|
||||
Error::BadRequest("Required to be authed in viewer mode".to_string())
|
||||
})?;
|
||||
(
|
||||
username.clone(),
|
||||
username_to_permissioned_as(&username),
|
||||
email,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Ok((username, permissioned_as, email))
|
||||
}
|
||||
|
||||
async fn execute_component(
|
||||
OptAuthed(opt_authed): OptAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -1196,7 +1129,6 @@ async fn execute_component(
|
||||
triggerables_v2: Some(hm),
|
||||
on_behalf_of: None,
|
||||
on_behalf_of_email: None,
|
||||
s3_inputs: None,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -1214,8 +1146,41 @@ async fn execute_component(
|
||||
}
|
||||
};
|
||||
|
||||
let (username, permissioned_as, email) =
|
||||
get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?;
|
||||
let (username, permissioned_as, email) = match policy.execution_mode {
|
||||
ExecutionMode::Anonymous => {
|
||||
let username = opt_authed
|
||||
.as_ref()
|
||||
.map(|a| a.username.clone())
|
||||
.unwrap_or_else(|| "anonymous".to_string());
|
||||
let (permissioned_as, email) = get_on_behalf_of(&policy)?;
|
||||
(username, permissioned_as, email)
|
||||
}
|
||||
ExecutionMode::Publisher => {
|
||||
let username = opt_authed
|
||||
.as_ref()
|
||||
.map(|a| a.username.clone())
|
||||
.ok_or_else(|| {
|
||||
Error::BadRequest(
|
||||
"publisher execution mode requires authentication".to_string(),
|
||||
)
|
||||
})?;
|
||||
let (permissioned_as, email) = get_on_behalf_of(&policy)?;
|
||||
(username, permissioned_as, email)
|
||||
}
|
||||
ExecutionMode::Viewer => {
|
||||
let (username, email) = opt_authed
|
||||
.as_ref()
|
||||
.map(|a| (a.username.clone(), a.email.clone()))
|
||||
.ok_or_else(|| {
|
||||
Error::BadRequest("Required to be authed in viewer mode".to_string())
|
||||
})?;
|
||||
(
|
||||
username.clone(),
|
||||
username_to_permissioned_as(&username),
|
||||
email,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let (job_payload, (args, job_id), tag) = match payload {
|
||||
ExecuteApp { args, component, raw_code: Some(raw_code), path: None, .. } => {
|
||||
@@ -1284,252 +1249,6 @@ async fn execute_component(
|
||||
Ok(uuid.to_string())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "parquet"))]
|
||||
async fn upload_s3_file_from_app() -> Result<()> {
|
||||
return Err(Error::BadRequest(
|
||||
"This endpoint requires the parquet feature to be enabled".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
struct UploadFileToS3Query {
|
||||
file_key: Option<String>,
|
||||
file_extension: Option<String>,
|
||||
s3_resource_path: Option<String>,
|
||||
content_type: Option<String>,
|
||||
content_disposition: Option<String>,
|
||||
force_viewer_file_key_regex: Option<String>,
|
||||
force_viewer_allow_user_resources: Option<bool>,
|
||||
force_viewer_allow_workspace_resource: Option<bool>,
|
||||
force_viewer_allowed_resources: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
async fn upload_s3_file_from_app(
|
||||
OptAuthed(opt_authed): OptAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Query(query): Query<UploadFileToS3Query>,
|
||||
request: axum::extract::Request,
|
||||
) -> JsonResult<UploadFileResponse> {
|
||||
let policy = if let Some(file_key_regex) = query.force_viewer_file_key_regex {
|
||||
Some(Policy {
|
||||
execution_mode: ExecutionMode::Viewer,
|
||||
triggerables: None,
|
||||
triggerables_v2: None,
|
||||
on_behalf_of: None,
|
||||
on_behalf_of_email: None,
|
||||
s3_inputs: Some(vec![S3Input {
|
||||
file_key_regex: file_key_regex,
|
||||
allow_user_resources: query.force_viewer_allow_user_resources.unwrap_or(false),
|
||||
allow_workspace_resource: query
|
||||
.force_viewer_allow_workspace_resource
|
||||
.unwrap_or(false),
|
||||
allowed_resources: query
|
||||
.force_viewer_allowed_resources
|
||||
.map(|s| s.split(',').map(|s| s.to_string()).collect())
|
||||
.unwrap_or_default(),
|
||||
}]),
|
||||
})
|
||||
} else {
|
||||
let policy_o = sqlx::query_scalar!(
|
||||
"SELECT policy from app WHERE path = $1 AND workspace_id = $2",
|
||||
&path.0,
|
||||
&w_id
|
||||
)
|
||||
.fetch_optional(&db)
|
||||
.await?;
|
||||
|
||||
policy_o
|
||||
.map(|p| serde_json::from_value::<Policy>(p).map_err(to_anyhow))
|
||||
.transpose()?
|
||||
};
|
||||
|
||||
let user_db = UserDB::new(db.clone());
|
||||
|
||||
let (s3_resource_opt, file_key) = if policy.as_ref().is_some_and(|p| p.s3_inputs.is_some()) {
|
||||
let policy = policy.unwrap();
|
||||
let s3_inputs = policy.s3_inputs.as_ref().unwrap();
|
||||
|
||||
let (username, permissioned_as, email) =
|
||||
get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?;
|
||||
|
||||
let on_behalf_authed = fetch_api_authed_from_permissioned_as(
|
||||
permissioned_as,
|
||||
email,
|
||||
&w_id,
|
||||
&db,
|
||||
Some(username),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(file_key) = query.file_key {
|
||||
// file key is provided => requires workspace, user or list policy and must match the regex
|
||||
let matching_s3_inputs = if let Some(ref s3_resource_path) = query.s3_resource_path {
|
||||
s3_inputs
|
||||
.iter()
|
||||
.filter(|s3_input| {
|
||||
s3_input.allowed_resources.contains(s3_resource_path)
|
||||
|| s3_input.allow_user_resources
|
||||
})
|
||||
.sorted_by_key(|i| i.allow_user_resources) // consider user resources last
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
s3_inputs
|
||||
.iter()
|
||||
.filter(|s3_input| s3_input.allow_workspace_resource)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let matched_input = matching_s3_inputs.iter().find(|s3_input| {
|
||||
match Regex::new(&s3_input.file_key_regex) {
|
||||
Ok(re) => re.is_match(&file_key),
|
||||
Err(e) => {
|
||||
tracing::error!("Error compiling regex: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(matched_input) = matched_input {
|
||||
if let Some(ref s3_resource_path) = query.s3_resource_path {
|
||||
if matched_input.allow_user_resources {
|
||||
if let Some(authed) = opt_authed {
|
||||
(
|
||||
Some(
|
||||
get_s3_resource(
|
||||
&authed,
|
||||
&db,
|
||||
Some(user_db),
|
||||
"",
|
||||
&w_id,
|
||||
s3_resource_path,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
file_key,
|
||||
)
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
"User resources are not allowed without being logged in"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
(
|
||||
Some(
|
||||
get_s3_resource(
|
||||
&on_behalf_authed,
|
||||
&db,
|
||||
Some(user_db),
|
||||
"",
|
||||
&w_id,
|
||||
s3_resource_path,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
file_key,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let (_, s3_resource_opt) =
|
||||
get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None)
|
||||
.await?;
|
||||
(s3_resource_opt, file_key)
|
||||
}
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
"No matching s3 resource found for the given file key".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// no file key => requires unnamed upload policy => allow workspace resource and file_key_regex is empty
|
||||
let has_unnamed_policy = s3_inputs.iter().any(|s3_input| {
|
||||
s3_input.allow_workspace_resource && s3_input.file_key_regex.is_empty()
|
||||
});
|
||||
|
||||
if !has_unnamed_policy {
|
||||
return Err(Error::BadRequest(
|
||||
"no policy found for unnamed s3 file uplooad".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// for now, we place all files into `windmill_uploads` folder with a random name
|
||||
// TODO: make the folder configurable via the workspace settings
|
||||
let file_key = get_random_file_name(query.file_extension);
|
||||
|
||||
let (_, s3_resource_opt) =
|
||||
get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None).await?;
|
||||
|
||||
(s3_resource_opt, file_key)
|
||||
}
|
||||
} else {
|
||||
// backward compatibility (no policy)
|
||||
// if no policy but logged in, use the user's auth to get the s3 resource
|
||||
if let Some(authed) = opt_authed {
|
||||
let file_key = query
|
||||
.file_key
|
||||
.unwrap_or_else(|| get_random_file_name(query.file_extension));
|
||||
|
||||
if let Some(ref s3_resource_path) = query.s3_resource_path {
|
||||
(
|
||||
Some(
|
||||
get_s3_resource(
|
||||
&authed,
|
||||
&db,
|
||||
Some(user_db),
|
||||
"",
|
||||
&w_id,
|
||||
s3_resource_path,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
file_key,
|
||||
)
|
||||
} else {
|
||||
let (_, s3_resource) =
|
||||
get_workspace_s3_resource(&authed, &db, None, "", &w_id, None).await?;
|
||||
|
||||
(s3_resource, file_key)
|
||||
}
|
||||
} else {
|
||||
return Err(Error::BadRequest("Missing s3 policy".to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
let s3_resource = s3_resource_opt.ok_or(Error::InternalErr(
|
||||
"No files storage resource defined at the workspace level".to_string(),
|
||||
))?;
|
||||
let s3_client = build_object_store_client(&s3_resource).await?;
|
||||
|
||||
let options = Attributes::from_iter(vec![
|
||||
(
|
||||
Attribute::ContentType,
|
||||
query.content_type.unwrap_or_else(|| {
|
||||
mime_guess::from_path(&file_key)
|
||||
.first_or_octet_stream()
|
||||
.to_string()
|
||||
}),
|
||||
),
|
||||
(
|
||||
Attribute::ContentDisposition,
|
||||
query.content_disposition.unwrap_or("inline".to_string()),
|
||||
),
|
||||
])
|
||||
.into();
|
||||
|
||||
upload_file_internal(s3_client, &file_key, request, options).await?;
|
||||
|
||||
return Ok(Json(UploadFileResponse { file_key }));
|
||||
}
|
||||
|
||||
fn get_on_behalf_of(policy: &Policy) -> Result<(String, String)> {
|
||||
let permissioned_as = policy
|
||||
.on_behalf_of
|
||||
|
||||
@@ -1209,7 +1209,6 @@ mod tests {
|
||||
.into(),
|
||||
hash: None,
|
||||
tag_override: None,
|
||||
is_trigger: None,
|
||||
}),
|
||||
stop_after_if: None,
|
||||
stop_after_all_iters_if: None,
|
||||
@@ -1237,7 +1236,6 @@ mod tests {
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
is_trigger: None,
|
||||
}),
|
||||
stop_after_if: Some(StopAfterIf {
|
||||
expr: "foo = 'bar'".to_string(),
|
||||
@@ -1292,7 +1290,6 @@ mod tests {
|
||||
input_transforms: HashMap::new(),
|
||||
hash: None,
|
||||
tag_override: None,
|
||||
is_trigger: None,
|
||||
}
|
||||
.into(),
|
||||
stop_after_if: Some(StopAfterIf {
|
||||
|
||||
@@ -23,7 +23,7 @@ use windmill_common::{
|
||||
utils::{not_found_if_none, StripPath},
|
||||
};
|
||||
|
||||
const KINDS: [&str; 12] = [
|
||||
const KINDS: [&str; 10] = [
|
||||
"script",
|
||||
"group_",
|
||||
"resource",
|
||||
@@ -34,8 +34,6 @@ const KINDS: [&str; 12] = [
|
||||
"app",
|
||||
"raw_app",
|
||||
"http_trigger",
|
||||
"websocket_trigger",
|
||||
"kafka_trigger",
|
||||
];
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
#[cfg(feature = "parquet")]
|
||||
use crate::job_helpers_ee::get_workspace_s3_resource;
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sql_builder::{bind::Bind, SqlBuilder};
|
||||
use sqlx::prelude::FromRow;
|
||||
use std::collections::HashMap;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use windmill_audit::{audit_ee::audit_log, ActionKind};
|
||||
use windmill_common::{
|
||||
db::UserDB,
|
||||
error::{self, JsonResult},
|
||||
utils::{not_found_if_none, paginate, require_admin, Pagination, StripPath},
|
||||
worker::{to_raw_value, CLOUD_HOSTED},
|
||||
};
|
||||
use windmill_queue::PushArgsOwned;
|
||||
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
jobs::{
|
||||
@@ -8,31 +27,6 @@ use crate::{
|
||||
},
|
||||
users::{fetch_api_authed, OptAuthed},
|
||||
};
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
#[cfg(feature = "parquet")]
|
||||
use http::header::IF_NONE_MATCH;
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sql_builder::{bind::Bind, SqlBuilder};
|
||||
use sqlx::prelude::FromRow;
|
||||
use std::collections::HashMap;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use windmill_audit::{audit_ee::audit_log, ActionKind};
|
||||
#[cfg(feature = "parquet")]
|
||||
use windmill_common::s3_helpers::build_object_store_client;
|
||||
use windmill_common::{
|
||||
db::UserDB,
|
||||
error::{self, JsonResult},
|
||||
s3_helpers::S3Object,
|
||||
utils::{not_found_if_none, paginate, require_admin, Pagination, StripPath},
|
||||
worker::{to_raw_value, CLOUD_HOSTED},
|
||||
};
|
||||
use windmill_queue::PushArgsOwned;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref ROUTE_PATH_KEY_RE: regex::Regex = regex::Regex::new(r"/:\w+").unwrap();
|
||||
@@ -105,7 +99,6 @@ struct NewTrigger {
|
||||
is_async: bool,
|
||||
requires_auth: bool,
|
||||
http_method: HttpMethod,
|
||||
static_asset_config: Option<sqlx::types::Json<S3Object>>,
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
@@ -123,7 +116,6 @@ struct Trigger {
|
||||
is_async: bool,
|
||||
requires_auth: bool,
|
||||
http_method: HttpMethod,
|
||||
static_asset_config: Option<sqlx::types::Json<S3Object>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -135,7 +127,6 @@ struct EditTrigger {
|
||||
is_async: bool,
|
||||
requires_auth: bool,
|
||||
http_method: HttpMethod,
|
||||
static_asset_config: Option<sqlx::types::Json<S3Object>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -191,7 +182,7 @@ async fn get_trigger(
|
||||
let path = path.to_path();
|
||||
let trigger = sqlx::query_as!(
|
||||
Trigger,
|
||||
r#"SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as "http_method: _", edited_by, email, edited_at, extra_perms, is_async, requires_auth, static_asset_config as "static_asset_config: _"
|
||||
r#"SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as "http_method: _", edited_by, email, edited_at, extra_perms, is_async, requires_auth
|
||||
FROM http_trigger
|
||||
WHERE workspace_id = $1 AND path = $2"#,
|
||||
w_id,
|
||||
@@ -218,7 +209,7 @@ async fn create_trigger(
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
sqlx::query!(
|
||||
"INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, static_asset_config, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now())",
|
||||
"INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now())",
|
||||
w_id,
|
||||
ct.path,
|
||||
ct.route_path,
|
||||
@@ -227,8 +218,7 @@ async fn create_trigger(
|
||||
ct.is_flow,
|
||||
ct.is_async,
|
||||
ct.requires_auth,
|
||||
ct.http_method as _,
|
||||
ct.static_asset_config as _,
|
||||
ct.http_method as HttpMethod,
|
||||
&authed.username,
|
||||
&authed.email
|
||||
)
|
||||
@@ -271,15 +261,14 @@ async fn update_trigger(
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE http_trigger
|
||||
SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, static_asset_config = $7, edited_by = $8, email = $9, is_async = $10, requires_auth = $11, edited_at = now()
|
||||
WHERE workspace_id = $12 AND path = $13",
|
||||
SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, edited_by = $7, email = $8, is_async = $9, requires_auth = $10, edited_at = now()
|
||||
WHERE workspace_id = $11 AND path = $12",
|
||||
ct.route_path,
|
||||
&route_path_key,
|
||||
ct.script_path,
|
||||
ct.path,
|
||||
ct.is_flow,
|
||||
ct.http_method as _,
|
||||
ct.static_asset_config as _,
|
||||
ct.http_method as HttpMethod,
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
ct.is_async,
|
||||
@@ -290,13 +279,12 @@ async fn update_trigger(
|
||||
.execute(&mut *tx).await?;
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, static_asset_config = $5, edited_by = $6, email = $7, is_async = $8, requires_auth = $9, edited_at = now()
|
||||
WHERE workspace_id = $10 AND path = $11",
|
||||
"UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, edited_by = $5, email = $6, is_async = $7, requires_auth = $8, edited_at = now()
|
||||
WHERE workspace_id = $9 AND path = $10",
|
||||
ct.script_path,
|
||||
ct.path,
|
||||
ct.is_flow,
|
||||
ct.http_method as _,
|
||||
ct.static_asset_config as _,
|
||||
ct.http_method as HttpMethod,
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
ct.is_async,
|
||||
@@ -417,7 +405,6 @@ struct TriggerRoute {
|
||||
edited_by: String,
|
||||
email: String,
|
||||
http_method: HttpMethod,
|
||||
static_asset_config: Option<sqlx::types::Json<S3Object>>,
|
||||
}
|
||||
|
||||
async fn get_http_route_trigger(
|
||||
@@ -434,7 +421,7 @@ async fn get_http_route_trigger(
|
||||
let route_path = StripPath(splitted.collect::<Vec<_>>().join("/"));
|
||||
let triggers = sqlx::query_as!(
|
||||
TriggerRoute,
|
||||
r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _", static_asset_config as "static_asset_config: _" FROM http_trigger WHERE workspace_id = $1"#,
|
||||
r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _" FROM http_trigger WHERE workspace_id = $1"#,
|
||||
w_id
|
||||
)
|
||||
.fetch_all(db)
|
||||
@@ -443,7 +430,7 @@ async fn get_http_route_trigger(
|
||||
} else {
|
||||
let triggers = sqlx::query_as!(
|
||||
TriggerRoute,
|
||||
r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _", static_asset_config as "static_asset_config: _" FROM http_trigger"#,
|
||||
r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _" FROM http_trigger"#,
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
@@ -507,7 +494,7 @@ async fn get_http_route_trigger(
|
||||
trigger.email.clone(),
|
||||
&trigger.workspace_id,
|
||||
&db,
|
||||
Some(username_override.unwrap_or("anonymous".to_string())),
|
||||
username_override.unwrap_or("anonymous".to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -531,90 +518,6 @@ async fn route_job(
|
||||
Ok(trigger) => trigger,
|
||||
Err(e) => return e.into_response(),
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "parquet"))]
|
||||
if trigger.static_asset_config.is_some() {
|
||||
return error::Error::InternalErr(
|
||||
"Static asset configuration is not supported in this build".to_string(),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
if let Some(sqlx::types::Json(config)) = trigger.static_asset_config {
|
||||
let build_static_response_f = async {
|
||||
let (_, s3_resource_opt) = get_workspace_s3_resource(
|
||||
&authed,
|
||||
&db,
|
||||
None,
|
||||
&"NO_TOKEN".to_string(), // no token is provided in this case
|
||||
&trigger.workspace_id,
|
||||
config.storage,
|
||||
)
|
||||
.await?;
|
||||
let s3_resource = s3_resource_opt.ok_or(error::Error::InternalErr(
|
||||
"No files storage resource defined at the workspace level".to_string(),
|
||||
))?;
|
||||
let s3_client = build_object_store_client(&s3_resource).await?;
|
||||
let path = object_store::path::Path::from(config.s3);
|
||||
let s3_object = s3_client.get(&path).await.map_err(|err| {
|
||||
tracing::warn!("Error retrieving file from S3: {:?}", err);
|
||||
error::Error::InternalErr(format!("Error retrieving file: {}", err.to_string()))
|
||||
})?;
|
||||
let mut response_headers = http::HeaderMap::new();
|
||||
if let Some(ref e_tag) = s3_object.meta.e_tag {
|
||||
if let Some(if_none_match) = headers.get(IF_NONE_MATCH) {
|
||||
if if_none_match == e_tag {
|
||||
return Ok::<_, error::Error>((
|
||||
StatusCode::NOT_MODIFIED,
|
||||
response_headers,
|
||||
axum::body::Body::empty(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Ok(e_tag) = e_tag.parse() {
|
||||
response_headers.insert("etag", e_tag);
|
||||
}
|
||||
}
|
||||
response_headers.insert(
|
||||
"content-type",
|
||||
s3_object
|
||||
.attributes
|
||||
.get(&object_store::Attribute::ContentType)
|
||||
.map(|s| s.parse().ok())
|
||||
.flatten()
|
||||
.unwrap_or("application/octet-stream".parse().unwrap()),
|
||||
);
|
||||
response_headers.insert(
|
||||
"content-disposition",
|
||||
config.filename.as_ref().map_or_else(
|
||||
|| {
|
||||
s3_object
|
||||
.attributes
|
||||
.get(&object_store::Attribute::ContentDisposition)
|
||||
.map(|s| s.parse().ok())
|
||||
.flatten()
|
||||
.unwrap_or("inline".parse().unwrap())
|
||||
},
|
||||
|filename| {
|
||||
format!("inline; filename=\"{}\"", filename)
|
||||
.parse()
|
||||
.unwrap_or("inline".parse().unwrap())
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let body_stream = axum::body::Body::from_stream(s3_object.into_stream());
|
||||
Ok::<_, error::Error>((StatusCode::OK, response_headers, body_stream))
|
||||
};
|
||||
match build_static_response_f.await {
|
||||
Ok((status, headers, body_stream)) => {
|
||||
return (status, headers, body_stream).into_response()
|
||||
}
|
||||
Err(e) => return e.into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
let headers = headers
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user