mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 00:06:14 +00:00
Merge branch 'main' into python-uv
This commit is contained in:
@@ -1,22 +1,6 @@
|
||||
ARG DEBIAN_IMAGE=debian:bookworm-slim
|
||||
ARG RUST_IMAGE=rust:1.80-slim-bookworm
|
||||
ARG PYTHON_IMAGE=python:3.11.4-slim-bookworm
|
||||
|
||||
FROM ${DEBIAN_IMAGE} as downloader
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
RUN apt update -y
|
||||
RUN apt install -y unzip curl
|
||||
|
||||
RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.46.3/deno-x86_64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.46.3/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
|
||||
|
||||
RUN unzip deno.zip && rm deno.zip
|
||||
|
||||
|
||||
FROM ${RUST_IMAGE} as builder
|
||||
|
||||
@@ -31,7 +15,7 @@ ENV SQLX_OFFLINE=true
|
||||
|
||||
RUN mkdir -p /frontend/build
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates tzdata libpq5 cmake\
|
||||
&& apt-get install -y ca-certificates tzdata libpq5 cmake unzip\
|
||||
make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev \
|
||||
libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libxml2-dev \
|
||||
libxmlsec1-dev libffi-dev liblzma-dev mecab-ipadic-utf8 libgdbm-dev libc6-dev git libprotobuf-dev libnl-route-3-dev \
|
||||
@@ -56,13 +40,14 @@ RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VER
|
||||
|
||||
RUN /usr/local/bin/python3 -m pip install pip-tools
|
||||
|
||||
COPY --from=oven/bun:1.1.27 /usr/local/bin/bun /usr/bin/bun
|
||||
COPY --from=oven/bun:1.1.30 /usr/local/bin/bun /usr/bin/bun
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.41.0/deno-x86_64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.41.0/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
RUN curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.0/deno-x86_64-unknown-linux-gnu.zip -o deno.zip
|
||||
# RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.0/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
|
||||
COPY --from=downloader --chmod=755 /deno /usr/bin/deno
|
||||
RUN unzip deno.zip && rm deno.zip && mv deno /usr/bin/deno
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y postgresql-client --allow-unauthenticated
|
||||
|
||||
@@ -41,6 +41,10 @@ jobs:
|
||||
- name: cargo test
|
||||
timeout-minutes: 15
|
||||
run:
|
||||
/usr/bin/deno --version &&
|
||||
/usr/bin/bun -v &&
|
||||
go version &&
|
||||
/usr/local/bin/python3 --version &&
|
||||
mkdir frontend/build && cd backend && touch
|
||||
windmill-api/openapi-deref.yaml &&
|
||||
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
name: Build and publish windmill for RHEL9
|
||||
on:
|
||||
workflow_dispatch
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
build_ee:
|
||||
runs-on: ubicloud
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Read EE repo commit hash
|
||||
run: |
|
||||
echo "ee_repo_ref=$(cat ./backend/ee-repo-ref.txt)" >> "$GITHUB_ENV"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: windmill-labs/windmill-ee-private
|
||||
path: ./windmill-ee-private
|
||||
ref: ${{ env.ee_repo_ref }}
|
||||
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
fetch-depth: 0
|
||||
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v2
|
||||
- uses: depot/setup-action@v1
|
||||
|
||||
- name: Docker meta
|
||||
id: meta-ee-public
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-rhel9
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=sha
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Substitute EE code
|
||||
run: |
|
||||
./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
|
||||
|
||||
- name: Copy RHEL9 Dockerfile
|
||||
run: |
|
||||
cp ./docker/RHEL9/Dockerfile ./Dockerfile
|
||||
|
||||
- name: Build and push publicly ee amd64
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
tags: |
|
||||
${{ steps.meta-ee-public.outputs.tags }}-amd64
|
||||
labels: |
|
||||
${{ steps.meta-ee-public.outputs.labels }}-amd64
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
- name: Build and push publicly ee arm64
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
tags: |
|
||||
${{ steps.meta-ee-public.outputs.tags }}-arm64
|
||||
labels: |
|
||||
${{ steps.meta-ee-public.outputs.labels }}-arm64
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract-ee-amd64
|
||||
with:
|
||||
image: ${{ steps.meta-ee-public.outputs.tags}}-amd64
|
||||
path: "/windmill/target/release/windmill"
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract-ee-arm64
|
||||
with:
|
||||
image: ${{ steps.meta-ee-public.outputs.tags}}-arm64
|
||||
path: "/windmill/target/release/windmill"
|
||||
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
mv "${{ steps.extract-ee-amd64.outputs.destination }}/windmill" "${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9"
|
||||
mv "${{ steps.extract-ee-arm64.outputs.destination }}/windmill" "${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9"
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RHEL9-amd64 build
|
||||
path: ${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RHEL9-arm64 build
|
||||
path: ${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9
|
||||
|
||||
# - name: Attach binary to release
|
||||
# uses: softprops/action-gh-release@v2
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# with:
|
||||
# files: |
|
||||
# ${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9
|
||||
# ${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9
|
||||
@@ -394,7 +394,7 @@ jobs:
|
||||
verify_ee_image_vulnerabilities:
|
||||
runs-on: ubicloud
|
||||
needs: [tag_latest_ee]
|
||||
# if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -589,8 +589,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-cuda
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -635,8 +633,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-slim
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -680,8 +676,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-slim
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -726,8 +720,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-full
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -771,8 +763,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-full
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
@@ -1,5 +1,70 @@
|
||||
# Changelog
|
||||
|
||||
## [1.409.2](https://github.com/windmill-labs/windmill/compare/v1.409.1...v1.409.2) (2024-10-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add extra args support for exception to bun scripts ([1466da3](https://github.com/windmill-labs/windmill/commit/1466da3999add0238b9c42ca13df52194f082fc0))
|
||||
* fix script persistence in url + add support for extra error args in python ([3174024](https://github.com/windmill-labs/windmill/commit/3174024d8e6ecbe9f8c9e1ea055d611f652c0057))
|
||||
|
||||
## [1.409.1](https://github.com/windmill-labs/windmill/compare/v1.409.0...v1.409.1) (2024-10-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **apidocs:** fix generated openapi files ([d24e153](https://github.com/windmill-labs/windmill/commit/d24e1530655d27ffda9bb4c19471dc1431124cc2))
|
||||
* **git-sync:** propagate update of folders with git sync ([6abb346](https://github.com/windmill-labs/windmill/commit/6abb346013da4a907a860713a8a67642985b8025))
|
||||
|
||||
## [1.409.0](https://github.com/windmill-labs/windmill/compare/v1.408.1...v1.409.0) (2024-10-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** unify all triggers UX and simplify flow settings ([#4259](https://github.com/windmill-labs/windmill/issues/4259)) ([91a3d06](https://github.com/windmill-labs/windmill/commit/91a3d065298cce7a882464fa0cd31d8f1ae9dda2))
|
||||
* Scroll to element in virtual list when clicking on graph point ([#4532](https://github.com/windmill-labs/windmill/issues/4532)) ([7126ba1](https://github.com/windmill-labs/windmill/commit/7126ba12c7eb52d2cfbe8d83311b5592a5707bce))
|
||||
* **sso:** adding the ability to define a custom display name for sso ([#4529](https://github.com/windmill-labs/windmill/issues/4529)) ([99c5b3e](https://github.com/windmill-labs/windmill/commit/99c5b3ecdacb1158c2cba5b891c4c3b8b70c3b6a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add indexer backup lock to fit the deployment model ([#4531](https://github.com/windmill-labs/windmill/issues/4531)) ([411bce7](https://github.com/windmill-labs/windmill/commit/411bce7e13aabfa53db80d55261ea4511f6d1ae9))
|
||||
* **app:** accept connecting to non yet existing state output for convenience ([9eb1ecc](https://github.com/windmill-labs/windmill/commit/9eb1ecc9f3017e2f4284a827a2bcd21f36b1b8ac))
|
||||
* **app:** improve absolute url handling in download button and downloadFile ([dcdbf1a](https://github.com/windmill-labs/windmill/commit/dcdbf1afb4d5a18e00b9bbb1eb0bef129ea5667f))
|
||||
* **app:** make s3 uploads persistent across tabs change ([c3b536b](https://github.com/windmill-labs/windmill/commit/c3b536b1b8069898131768867a187b186b21e537))
|
||||
* canceled jobs button reporting 0 jobs cancelled ([#4534](https://github.com/windmill-labs/windmill/issues/4534)) ([e736572](https://github.com/windmill-labs/windmill/commit/e736572db10929ae5e123c4f6cef73b8e90fc29b))
|
||||
* **python-client:** improve get_job_status for running jobs ([a8c4ea2](https://github.com/windmill-labs/windmill/commit/a8c4ea2334d2535fa7d5d43f58d65565afe8f3e5))
|
||||
* **ui:** dark mode support for queue metrics based critical alert ([#4535](https://github.com/windmill-labs/windmill/issues/4535)) ([f38b3d1](https://github.com/windmill-labs/windmill/commit/f38b3d14e8092ae58817511aea91a4e77725ead6))
|
||||
|
||||
## [1.408.1](https://github.com/windmill-labs/windmill/compare/v1.408.0...v1.408.1) (2024-10-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix deno cache --allow-import on deno 2 ([42fe31f](https://github.com/windmill-labs/windmill/commit/42fe31f804c9e6643cd90167494e45270831e013))
|
||||
|
||||
## [1.408.0](https://github.com/windmill-labs/windmill/compare/v1.407.2...v1.408.0) (2024-10-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **app builder:** file download helper ([#4511](https://github.com/windmill-labs/windmill/issues/4511)) ([f82f091](https://github.com/windmill-labs/windmill/commit/f82f09129096cfff975370d8bb7b6d832a2b8f9f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** handle case where 'toString' is a schema field ([568cc66](https://github.com/windmill-labs/windmill/commit/568cc66932fb0470f5e89de7b02d94dba4050638))
|
||||
* **frontend:** s3 file uploader works on public apps too ([982dde2](https://github.com/windmill-labs/windmill/commit/982dde2b9dfe6d9eda300c683af729d97a03cb4d))
|
||||
* **frontend:** set unused schema property fields to null ([be11240](https://github.com/windmill-labs/windmill/commit/be112408e7c4601314726e9517c37daaeaa1bf09))
|
||||
* improve workflow as code row-lock on db to handle more concurrency ([d2c4d3f](https://github.com/windmill-labs/windmill/commit/d2c4d3fa207379cb0b8ac180f6ccc759045580e8))
|
||||
|
||||
## [1.407.2](https://github.com/windmill-labs/windmill/compare/v1.407.1...v1.407.2) (2024-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve default properties of new nodes of flows (suspend, branchone, branchall) ([d9bdc5a](https://github.com/windmill-labs/windmill/commit/d9bdc5a5b08dd4d0381304656af097315398c9d4))
|
||||
|
||||
## [1.407.1](https://github.com/windmill-labs/windmill/compare/v1.407.0...v1.407.1) (2024-10-10)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -177,7 +177,7 @@ COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
|
||||
|
||||
COPY --from=denoland/deno:2.0.0 --chmod=755 /usr/bin/deno /usr/bin/deno
|
||||
|
||||
COPY --from=oven/bun:1.1.27 /usr/local/bin/bun /usr/bin/bun
|
||||
COPY --from=oven/bun:1.1.30 /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
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO concurrency_locks (id, last_locked_at, owner)\n VALUES ($1, now(), $2)\n ON CONFLICT (id)\n DO UPDATE SET\n last_locked_at = now(),\n owner = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d"
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM schedule WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1ca5bc2d35c0498b587fd0618434def64233dc4f8fc3344d8d74be8e96ded659"
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM http_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "31bc3dcea29be9cc0242771d25a232f173446d29c08fc29ddb8d55294f2c070e"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE concurrency_locks SET\n last_locked_at = now()\n WHERE id = $1 AND owner = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "57e270e032e8c04dda7b5c1ca949861756b3ad367a4a500728332a7cb91560a4"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5fc6b4a4dbb7875bdec76f876c18543435a95b019b20081f52f6ed6f4457e3c7"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT owner FROM concurrency_locks WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "owner",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5fd70c70ce52cbc51fa9124cb05f82b5951f17d1b7eade53c6d89253d55f8b9f"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "a7f5431e3b8960e9dc46fae69dd4391516d8b169186548ec44528c84078b80d8"
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT schedule FROM schedule WHERE path = $1 AND script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "schedule",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c060b8bbc5af7d2e7d0aaff64f0f62ec9db58611a99b0ba7f0375638b128ab89"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin, scopes)\n VALUES ($1, $2, $3, $4, $5, $6)",
|
||||
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin, scopes, workspace_id)\n VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -10,10 +10,11 @@
|
||||
"Varchar",
|
||||
"Timestamptz",
|
||||
"Bool",
|
||||
"TextArray"
|
||||
"TextArray",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "34ad8a2a5bd89b9b8e25847a7e5e94ef99e35a178ad6328c1bcde2a6d6f88cb5"
|
||||
"hash": "c624f15f3e321b1eecf123da9bf0b18e8c1d16ef25ffb9d04e5447d0d583d55c"
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "label",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token_prefix",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "expiration",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "last_used_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "scopes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "c7ee7ce64686cef41cebd99ad7ef31572fc1bf12e6ae473fd58fafb025989965"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT last_locked_at\n FROM concurrency_locks\n WHERE id = $1\n FOR UPDATE",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "last_locked_at",
|
||||
"type_info": "Timestamp"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "cecf1addc4aecb087a14786b2a9165895ca61ef042947c7314f66514d7f29edc"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "d7a0f19f9e18d2ea49316012375ad78b69292ba091d69880945e42bebe890d66"
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "label",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token_prefix",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "expiration",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "last_used_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "scopes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "eff32aeac25a75d06f73e08c26dd3fd25f6b85cbea870505751c6a82457ae1da"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "f06e0e4fa358b26792df22fff48b71a6fcfa1e5603ea472892917c1accd1aafb"
|
||||
}
|
||||
Generated
+122
-108
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
@@ -486,9 +486,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.13"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429"
|
||||
checksum = "103db485efc3e41214fe4fda9f3dbeae2eb9082f48fd236e6095627a9422066e"
|
||||
dependencies = [
|
||||
"brotli 7.0.0",
|
||||
"bzip2",
|
||||
@@ -650,7 +650,7 @@ dependencies = [
|
||||
"hex",
|
||||
"hmac",
|
||||
"http-types",
|
||||
"hyper 0.14.30",
|
||||
"hyper 0.14.31",
|
||||
"hyper-tls 0.5.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -794,14 +794,14 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tracing",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.45.0"
|
||||
version = "1.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33ae899566f3d395cbf42858e433930682cc9c1889fa89318896082fef45efb"
|
||||
checksum = "0dc2faec3205d496c7e57eff685dd944203df7ce16a4116d0281c44021788a7b"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -821,9 +821,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.46.0"
|
||||
version = "1.47.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f39c09e199ebd96b9f860b0fce4b6625f211e064ad7c8693b72ecf7ef03881e0"
|
||||
checksum = "c93c241f52bc5e0476e259c953234dab7e2a35ee207ee202e86c0095ec4951dc"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -843,9 +843,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.45.0"
|
||||
version = "1.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d95f93a98130389eb6233b9d615249e543f6c24a68ca1f109af9ca5164a8765"
|
||||
checksum = "b259429be94a3459fa1b00c5684faee118d74f9577cc50aebadc36e507c63b5f"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -954,7 +954,7 @@ dependencies = [
|
||||
"http-body 0.4.6",
|
||||
"http-body 1.0.1",
|
||||
"httparse",
|
||||
"hyper 0.14.30",
|
||||
"hyper 0.14.31",
|
||||
"hyper-rustls 0.24.2",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
@@ -1043,7 +1043,7 @@ dependencies = [
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
@@ -1469,9 +1469,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
|
||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
@@ -1599,9 +1599,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.28"
|
||||
version = "1.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
|
||||
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -2233,7 +2233,7 @@ dependencies = [
|
||||
"arrow-array",
|
||||
"arrow-ipc",
|
||||
"arrow-schema",
|
||||
"async-compression 0.4.13",
|
||||
"async-compression 0.4.16",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"bzip2",
|
||||
@@ -2271,7 +2271,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"url",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"xz2",
|
||||
"zstd 0.13.2",
|
||||
]
|
||||
@@ -2371,7 +2371,7 @@ dependencies = [
|
||||
"regex",
|
||||
"sha2 0.10.8",
|
||||
"unicode-segmentation",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2538,7 +2538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2645,7 +2645,7 @@ dependencies = [
|
||||
"error_reporter",
|
||||
"http 1.1.0",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"hyper-rustls 0.27.3",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
@@ -2777,7 +2777,7 @@ checksum = "22a1abc6bd8af41aa2496ceceef94f4277092c781a9495c437327a17659553a2"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"deno_native_certs",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-tokio-stream",
|
||||
"rustls-webpki 0.102.8",
|
||||
@@ -2822,7 +2822,7 @@ dependencies = [
|
||||
"futures",
|
||||
"serde",
|
||||
"tokio",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3617,7 +3617,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.7",
|
||||
"dirs-next",
|
||||
"hyper 0.14.30",
|
||||
"hyper 0.14.31",
|
||||
"hyper-rustls 0.24.2",
|
||||
"ring 0.16.20",
|
||||
"rustls 0.21.12",
|
||||
@@ -4196,9 +4196,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.30"
|
||||
version = "0.14.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
|
||||
checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -4220,9 +4220,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
||||
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -4247,7 +4247,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.30",
|
||||
"hyper 0.14.31",
|
||||
"log",
|
||||
"rustls 0.21.12",
|
||||
"rustls-native-certs 0.6.3",
|
||||
@@ -4263,9 +4263,9 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"hyper-util",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"rustls-native-certs 0.8.0",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
@@ -4280,7 +4280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper 0.14.30",
|
||||
"hyper 0.14.31",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
@@ -4294,7 +4294,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
@@ -4313,7 +4313,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.1",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"pin-project-lite",
|
||||
"socket2 0.5.7",
|
||||
"tokio",
|
||||
@@ -4889,7 +4889,7 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"gethostname",
|
||||
"mail-builder",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"rustls-pki-types",
|
||||
"smtp-proto",
|
||||
"tokio",
|
||||
@@ -5223,7 +5223,7 @@ dependencies = [
|
||||
"subprocess",
|
||||
"thiserror",
|
||||
"time",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"zstd 0.13.2",
|
||||
]
|
||||
|
||||
@@ -5438,7 +5438,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
"humantime",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"itertools 0.13.0",
|
||||
"md-5 0.10.6",
|
||||
"parking_lot",
|
||||
@@ -5541,9 +5541,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.66"
|
||||
version = "0.10.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
|
||||
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
@@ -5573,9 +5573,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.103"
|
||||
version = "0.9.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -5742,9 +5742,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
@@ -6021,7 +6021,7 @@ dependencies = [
|
||||
"postgres-protocol",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6132,9 +6132,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -6365,7 +6365,7 @@ dependencies = [
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"socket2 0.5.7",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -6382,7 +6382,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"ring 0.17.8",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
@@ -6690,7 +6690,7 @@ dependencies = [
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.30",
|
||||
"hyper 0.14.31",
|
||||
"hyper-rustls 0.24.2",
|
||||
"hyper-tls 0.5.0",
|
||||
"ipnet",
|
||||
@@ -6728,7 +6728,7 @@ version = "0.12.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
|
||||
dependencies = [
|
||||
"async-compression 0.4.13",
|
||||
"async-compression 0.4.16",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
@@ -6738,7 +6738,7 @@ dependencies = [
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"hyper-rustls 0.27.3",
|
||||
"hyper-tls 0.6.0",
|
||||
"hyper-util",
|
||||
@@ -6751,7 +6751,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"rustls-native-certs 0.8.0",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
@@ -6844,7 +6844,7 @@ dependencies = [
|
||||
"rkyv_derive",
|
||||
"seahash",
|
||||
"tinyvec",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7051,9 +7051,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.14"
|
||||
version = "0.23.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8"
|
||||
checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -7122,9 +7122,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-tokio-stream"
|
||||
@@ -7133,7 +7133,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"socket2 0.5.7",
|
||||
"tokio",
|
||||
]
|
||||
@@ -7214,9 +7214,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -7264,7 +7264,7 @@ dependencies = [
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7302,7 +7302,7 @@ dependencies = [
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8016,7 +8016,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -8028,7 +8028,7 @@ dependencies = [
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
@@ -8112,7 +8112,7 @@ dependencies = [
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
@@ -8154,7 +8154,7 @@ dependencies = [
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
@@ -8180,7 +8180,7 @@ dependencies = [
|
||||
"sqlx-core",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8833,7 +8833,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"time",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -9041,7 +9041,7 @@ dependencies = [
|
||||
"tokio-rustls 0.24.1",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9272,7 +9272,7 @@ version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||
dependencies = [
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
@@ -9472,7 +9472,7 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
|
||||
dependencies = [
|
||||
"async-compression 0.4.13",
|
||||
"async-compression 0.4.16",
|
||||
"bitflags 2.6.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -9777,7 +9777,7 @@ checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"rand 0.8.5",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
@@ -9977,7 +9977,7 @@ dependencies = [
|
||||
"log",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"rustls 0.23.14",
|
||||
"rustls 0.23.15",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -10038,9 +10038,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"serde",
|
||||
@@ -10358,7 +10358,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -10387,7 +10387,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"windmill-api",
|
||||
"windmill-api-client",
|
||||
"windmill-common",
|
||||
@@ -10399,7 +10399,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -10426,7 +10426,7 @@ dependencies = [
|
||||
"hf-hub",
|
||||
"hmac",
|
||||
"http 1.1.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"itertools 0.13.0",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
@@ -10470,7 +10470,7 @@ dependencies = [
|
||||
"ulid",
|
||||
"url",
|
||||
"urlencoding",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
"windmill-git-sync",
|
||||
@@ -10483,7 +10483,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
@@ -10496,12 +10496,12 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 1.0.109",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -10514,7 +10514,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -10531,7 +10531,7 @@ dependencies = [
|
||||
"git-version",
|
||||
"hex",
|
||||
"hmac",
|
||||
"hyper 1.4.1",
|
||||
"hyper 1.5.0",
|
||||
"indexmap 2.6.0",
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
@@ -10554,12 +10554,13 @@ dependencies = [
|
||||
"tracing-flame",
|
||||
"tracing-loki",
|
||||
"tracing-subscriber",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"windmill-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"rsmq_async",
|
||||
@@ -10567,19 +10568,20 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tracing",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"windmill-common",
|
||||
"windmill-queue",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"object_store",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -10589,13 +10591,25 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-tar",
|
||||
"tracing",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"windmill-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.405.5"
|
||||
dependencies = [
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -10604,7 +10618,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10616,7 +10630,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -10628,7 +10642,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10640,7 +10654,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
@@ -10651,7 +10665,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
@@ -10662,7 +10676,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10680,7 +10694,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -10697,7 +10711,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10709,7 +10723,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10727,7 +10741,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wasm"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.15",
|
||||
@@ -10748,7 +10762,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -10758,7 +10772,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10784,14 +10798,14 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"ulid",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
@@ -10801,7 +10815,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10856,7 +10870,7 @@ dependencies = [
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"urlencoding",
|
||||
"uuid 1.10.0",
|
||||
"uuid 1.11.0",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
"windmill-git-sync",
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -14,6 +14,7 @@ members = [
|
||||
"./windmill-audit",
|
||||
"./windmill-git-sync",
|
||||
"./windmill-indexer",
|
||||
"./windmill-macros",
|
||||
"./parsers/windmill-parser",
|
||||
"./parsers/windmill-parser-ts",
|
||||
"./parsers/windmill-parser-wasm",
|
||||
@@ -23,11 +24,11 @@ members = [
|
||||
"./parsers/windmill-parser-py",
|
||||
"./parsers/windmill-parser-py-imports",
|
||||
"./parsers/windmill-sql-datatype-parser-wasm",
|
||||
"./parsers/windmill-parser-yaml",
|
||||
"./parsers/windmill-parser-yaml", "windmill-macros",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.407.1"
|
||||
version = "1.409.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -39,6 +40,9 @@ path = "./src/main.rs"
|
||||
opt-level = 0
|
||||
incremental = true
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise", "windmill-indexer/enterprise"]
|
||||
@@ -113,6 +117,7 @@ windmill-common = { path = "./windmill-common", default-features = false }
|
||||
windmill-audit = { path = "./windmill-audit" }
|
||||
windmill-git-sync = { path = "./windmill-git-sync" }
|
||||
windmill-indexer = {path = "./windmill-indexer"}
|
||||
windmill-macros = {path = "./windmill-macros"}
|
||||
windmill-parser = { path = "./parsers/windmill-parser" }
|
||||
windmill-parser-ts = { path = "./parsers/windmill-parser-ts" }
|
||||
windmill-parser-py = { path = "./parsers/windmill-parser-py" }
|
||||
@@ -278,6 +283,8 @@ triomphe = "^0"
|
||||
|
||||
tantivy = "0.22.0"
|
||||
|
||||
# Macro-related
|
||||
proc-macro2 = "1.0"
|
||||
pulldown-cmark = "0.9"
|
||||
toml = "0.7"
|
||||
syn = { version = "2.0.74", features = ["full"] }
|
||||
|
||||
@@ -1 +1 @@
|
||||
8e3fbda05392e641c15262924dc0df47cc42d036
|
||||
0428068e4fbbd1380a4d8bbaab5c8e7955decdb8
|
||||
|
||||
+1
-1
@@ -476,7 +476,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
|
||||
#[cfg(feature = "tantivy")]
|
||||
let (index_reader, index_writer) = if should_index_jobs {
|
||||
let (r, w) = windmill_indexer::indexer_ee::init_index().await?;
|
||||
let (r, w) = windmill_indexer::indexer_ee::init_index(&db).await?;
|
||||
(Some(r), Some(w))
|
||||
} else {
|
||||
(None, None)
|
||||
|
||||
@@ -2745,7 +2745,7 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
|
||||
"lock": null,
|
||||
"path": null,
|
||||
"type": "rawscript",
|
||||
"content": "import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main() {\n return \"Hello\"\n}\n",
|
||||
"content": "import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main() {\n return wmill\n}\n",
|
||||
"language": "deno",
|
||||
"input_transforms": {}
|
||||
},
|
||||
|
||||
+3070
-108
File diff suppressed because it is too large
Load Diff
+3832
-1016
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.407.1
|
||||
version: 1.409.2
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -2789,7 +2789,14 @@ paths:
|
||||
oauth:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
display_name:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
saml:
|
||||
type: string
|
||||
required:
|
||||
@@ -4013,6 +4020,42 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Script"
|
||||
|
||||
/w/{workspace}/scripts/get_triggers_count/{path}:
|
||||
get:
|
||||
summary: get triggers count of script
|
||||
operationId: getTriggersCountOfScript
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: triggers count
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TriggersCount"
|
||||
|
||||
/w/{workspace}/scripts/list_tokens/{path}:
|
||||
get:
|
||||
summary: get tokens with script scope
|
||||
operationId: listTokensOfScript
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: tokens list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TruncatedToken"
|
||||
|
||||
/w/{workspace}/scripts/get/draft/{path}:
|
||||
get:
|
||||
summary: get script by path with draft
|
||||
@@ -4617,6 +4660,43 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Flow"
|
||||
|
||||
/w/{workspace}/flows/get_triggers_count/{path}:
|
||||
get:
|
||||
summary: get triggers count of flow
|
||||
operationId: getTriggersCountOfFlow
|
||||
tags:
|
||||
- flow
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: triggers count
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TriggersCount"
|
||||
|
||||
/w/{workspace}/flows/list_tokens/{path}:
|
||||
get:
|
||||
summary: get tokens with flow scope
|
||||
operationId: listTokensOfFlow
|
||||
tags:
|
||||
- flow
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: tokens list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TruncatedToken"
|
||||
|
||||
|
||||
/w/{workspace}/flows/toggle_workspace_error_handler/{path}:
|
||||
post:
|
||||
summary: Toggle ON and OFF the workspace error handler for a given flow
|
||||
@@ -10247,6 +10327,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
required:
|
||||
- token_prefix
|
||||
- created_at
|
||||
@@ -10264,6 +10346,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
workspace_id:
|
||||
type: string
|
||||
|
||||
NewTokenImpersonate:
|
||||
type: object
|
||||
@@ -10275,6 +10359,8 @@ components:
|
||||
format: date-time
|
||||
impersonate_email:
|
||||
type: string
|
||||
workspace_id:
|
||||
type: string
|
||||
required:
|
||||
- impersonate_email
|
||||
|
||||
@@ -11111,6 +11197,23 @@ components:
|
||||
- requires_auth
|
||||
- http_method
|
||||
|
||||
TriggersCount:
|
||||
type: object
|
||||
properties:
|
||||
primary_schedule:
|
||||
type: object
|
||||
properties:
|
||||
schedule:
|
||||
type: string
|
||||
schedule_count:
|
||||
type: number
|
||||
http_routes_count:
|
||||
type: number
|
||||
webhook_count:
|
||||
type: number
|
||||
email_count:
|
||||
type: number
|
||||
|
||||
Group:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::triggers::{
|
||||
get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail,
|
||||
};
|
||||
use crate::utils::WithStarredInfoQuery;
|
||||
use crate::{
|
||||
db::DB,
|
||||
@@ -53,6 +56,8 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/update/*path", post(update_flow))
|
||||
.route("/archive/*path", post(archive_flow_by_path))
|
||||
.route("/delete/*path", delete(delete_flow_by_path))
|
||||
.route("/get_triggers_count/*path", get(get_triggers_count))
|
||||
.route("/list_tokens/*path", get(list_tokens))
|
||||
.route("/get/*path", get(get_flow_by_path))
|
||||
.route("/get/draft/*path", get(get_flow_by_path_w_draft))
|
||||
.route("/exists/*path", get(exists_flow_by_path))
|
||||
@@ -874,6 +879,22 @@ async fn update_flow(
|
||||
Ok(nf.path.to_string())
|
||||
}
|
||||
|
||||
async fn get_triggers_count(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<TriggersCount> {
|
||||
let path = path.to_path();
|
||||
get_triggers_count_internal(&db, &w_id, &path, true).await
|
||||
}
|
||||
|
||||
async fn list_tokens(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
|
||||
let path = path.to_path();
|
||||
list_tokens_internal(&db, &w_id, &path, true).await
|
||||
}
|
||||
|
||||
async fn get_flow_by_path(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
|
||||
@@ -275,8 +275,10 @@ pub fn require_is_owner(authed: &ApiAuthed, name: &str) -> Result<()> {
|
||||
|
||||
async fn update_folder(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, name)): Path<(String, String)>,
|
||||
Json(mut ng): Json<UpdateFolder>,
|
||||
) -> Result<String> {
|
||||
@@ -367,6 +369,18 @@ async fn update_folder(
|
||||
}
|
||||
}
|
||||
|
||||
handle_deployment_metadata(
|
||||
&authed.email,
|
||||
&authed.username,
|
||||
&db,
|
||||
&w_id,
|
||||
DeployedObject::Folder { path: format!("f/{}", name) },
|
||||
Some(format!("Folder '{}' updated", name)),
|
||||
rsmq,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
|
||||
@@ -24,7 +24,7 @@ use windmill_common::jobs::{
|
||||
format_completed_job_result, format_result, CompletedJobWithFormattedResult, FormattedResult,
|
||||
ENTRYPOINT_OVERRIDE,
|
||||
};
|
||||
use windmill_common::worker::TMP_DIR;
|
||||
use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "parquet"))]
|
||||
use windmill_common::scripts::PREVIEW_IS_CODEBASE_HASH;
|
||||
@@ -81,7 +81,7 @@ use windmill_common::{METRICS_DEBUG_ENABLED, METRICS_ENABLED};
|
||||
use windmill_common::{get_latest_deployed_hash_for_path, BASE_URL};
|
||||
use windmill_queue::{
|
||||
cancel_job, get_queued_job, get_result_by_id_from_running_flow, job_is_complete, push,
|
||||
DecodeQueries, PushArgs, PushArgsOwned, PushIsolationLevel, QueueTransaction,
|
||||
DecodeQueries, PushArgs, PushArgsOwned, PushIsolationLevel,
|
||||
};
|
||||
|
||||
#[cfg(feature = "prometheus")]
|
||||
@@ -544,8 +544,8 @@ pub async fn get_path_for_hash<'c>(
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub async fn get_path_tag_limits_cache_for_hash<'c, R: rsmq_async::RsmqConnection + Send>(
|
||||
tx: &mut QueueTransaction<'c, R>,
|
||||
pub async fn get_path_tag_limits_cache_for_hash(
|
||||
tx: &DB,
|
||||
w_id: &str,
|
||||
hash: i64,
|
||||
) -> error::Result<(
|
||||
@@ -1469,6 +1469,8 @@ async fn cancel_jobs(
|
||||
}
|
||||
}
|
||||
|
||||
uuids.extend(trivial_jobs);
|
||||
|
||||
Ok(Json(uuids))
|
||||
}
|
||||
|
||||
@@ -2810,7 +2812,6 @@ pub async fn run_flow_by_path_inner(
|
||||
let flow_path = flow_path.to_path();
|
||||
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (tag, dedicated_worker, has_preprocessor) = sqlx::query!(
|
||||
"SELECT tag, dedicated_worker, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor
|
||||
@@ -2821,7 +2822,7 @@ pub async fn run_flow_by_path_inner(
|
||||
flow_path,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.map(|x| (x.tag, x.dedicated_worker, x.has_preprocessor))
|
||||
.ok_or_else(|| {
|
||||
@@ -2834,7 +2835,7 @@ pub async fn run_flow_by_path_inner(
|
||||
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
@@ -2906,14 +2907,13 @@ pub async fn restart_flow(
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
check_license_key_valid().await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let completed_job = sqlx::query_as::<_, CompletedJob>(
|
||||
"SELECT *, result->'wm_labels' as labels from completed_job WHERE id = $1 and workspace_id = $2",
|
||||
)
|
||||
.bind(job_id)
|
||||
.bind(&w_id)
|
||||
.fetch_optional(&mut tx)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.with_context(|| "Unable to find completed job with the given job UUID")?;
|
||||
|
||||
@@ -2931,7 +2931,7 @@ pub async fn restart_flow(
|
||||
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3007,16 +3007,16 @@ pub async fn run_script_by_path_inner(
|
||||
|
||||
check_scopes(&authed, || format!("run:script/{script_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, _delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3049,6 +3049,11 @@ pub async fn run_script_by_path_inner(
|
||||
Ok((StatusCode::CREATED, uuid.to_string()))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WorkflowAsCodeQuery {
|
||||
pub skip_update: Option<bool>,
|
||||
}
|
||||
|
||||
pub async fn run_workflow_as_code(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -3056,15 +3061,38 @@ pub async fn run_workflow_as_code(
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, job_id, entrypoint)): Path<(String, Uuid, String)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
Query(wkflow_query): Query<WorkflowAsCodeQuery>,
|
||||
Json(task): Json<WorkflowTask>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
|
||||
let mut i = 1;
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
check_tag_available_for_workspace(&w_id, &run_query.tag).await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let job = get_queued_job(&job_id, &w_id, &db).await?;
|
||||
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let job = not_found_if_none(job, "Queued Job", &job_id.to_string())?;
|
||||
let (job_payload, tag, _delete_after_use, timeout) = match job.job_kind {
|
||||
JobKind::Preview => (
|
||||
@@ -3089,7 +3117,7 @@ pub async fn run_workflow_as_code(
|
||||
JobKind::Script => {
|
||||
script_path_to_payload(
|
||||
job.script_path(),
|
||||
&mut tx,
|
||||
&db,
|
||||
&w_id,
|
||||
run_query.skip_preprocessor,
|
||||
)
|
||||
@@ -3098,6 +3126,12 @@ pub async fn run_workflow_as_code(
|
||||
_ => return Err(anyhow::anyhow!("Not supported").into()),
|
||||
};
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let mut extra = HashMap::new();
|
||||
extra.insert(ENTRYPOINT_OVERRIDE.to_string(), to_raw_value(&entrypoint));
|
||||
|
||||
@@ -3106,7 +3140,21 @@ pub async fn run_workflow_as_code(
|
||||
|
||||
let tag = run_query.tag.clone().or(tag).or(Some(job.tag));
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let (uuid, mut tx) = push(
|
||||
&db,
|
||||
@@ -3133,14 +3181,39 @@ pub async fn run_workflow_as_code(
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"UPDATE queue SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($4::text))) WHERE id = $2 AND workspace_id = $3",
|
||||
uuid.to_string(),
|
||||
job_id,
|
||||
w_id,
|
||||
entrypoint
|
||||
).execute(&mut tx).await?;
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if !wkflow_query.skip_update.unwrap_or(false) {
|
||||
sqlx::query!(
|
||||
"UPDATE queue SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($4::text))) WHERE id = $2 AND workspace_id = $3",
|
||||
uuid.to_string(),
|
||||
job_id,
|
||||
w_id,
|
||||
entrypoint
|
||||
).execute(&mut tx).await?;
|
||||
} else {
|
||||
tracing::info!("Skipping update of flow status for job {job_id} in workspace {w_id}");
|
||||
}
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
}
|
||||
|
||||
Ok((StatusCode::CREATED, uuid.to_string()))
|
||||
}
|
||||
|
||||
@@ -3504,15 +3577,13 @@ pub async fn run_wait_result_job_by_path_get(
|
||||
let script_path = script_path.to_path();
|
||||
check_scopes(&authed, || format!("run:script/{script_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3629,15 +3700,13 @@ pub async fn run_wait_result_script_by_path_internal(
|
||||
let script_path = script_path.to_path();
|
||||
check_scopes(&authed, || format!("run:script/{script_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3689,8 +3758,6 @@ pub async fn run_wait_result_script_by_hash(
|
||||
|
||||
check_queue_too_long(&db, run_query.queue_limit).await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let hash = script_hash.0;
|
||||
let (
|
||||
path,
|
||||
@@ -3705,7 +3772,7 @@ pub async fn run_wait_result_script_by_hash(
|
||||
delete_after_use,
|
||||
timeout,
|
||||
has_preprocessor,
|
||||
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
|
||||
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
|
||||
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
|
||||
cache_ttl = Some(run_query_cache_ttl);
|
||||
}
|
||||
@@ -3714,7 +3781,7 @@ pub async fn run_wait_result_script_by_hash(
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3796,7 +3863,6 @@ pub async fn run_wait_result_flow_by_path_internal(
|
||||
let flow_path = flow_path.to_path();
|
||||
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
@@ -3809,7 +3875,7 @@ pub async fn run_wait_result_flow_by_path_internal(
|
||||
flow_path,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.map(|x| (x.tag, x.dedicated_worker, x.early_return, x.has_preprocessor))
|
||||
.ok_or_else(|| {
|
||||
@@ -3821,7 +3887,7 @@ pub async fn run_wait_result_flow_by_path_internal(
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -4127,10 +4193,10 @@ async fn run_dependencies_job(
|
||||
JsonRawValue::from_string("true".to_string()).unwrap(),
|
||||
);
|
||||
if language == ScriptLang::Bun {
|
||||
let annotation = windmill_common::worker::get_annotation_ts(&raw_code);
|
||||
let annotation = windmill_common::worker::TypeScriptAnnotations::parse(&raw_code);
|
||||
hm.insert(
|
||||
"npm_mode".to_string(),
|
||||
JsonRawValue::from_string(annotation.npm_mode.to_string()).unwrap(),
|
||||
JsonRawValue::from_string(annotation.npm.to_string()).unwrap(),
|
||||
);
|
||||
}
|
||||
(PushArgs { extra: Some(hm), args: &ehm }, deps)
|
||||
@@ -4293,7 +4359,6 @@ async fn add_batch_jobs(
|
||||
}
|
||||
}
|
||||
"flow" => {
|
||||
let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
|
||||
|
||||
let mut uuids: Vec<Uuid> = Vec::new();
|
||||
let payload = if let Some(ref fv) = batch_info.flow_value {
|
||||
@@ -4311,6 +4376,7 @@ async fn add_batch_jobs(
|
||||
))?
|
||||
}
|
||||
};
|
||||
let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
|
||||
for _ in 0..n {
|
||||
let ehm = HashMap::new();
|
||||
let (uuid, ntx) = push(
|
||||
@@ -4511,7 +4577,6 @@ pub async fn run_job_by_hash_inner(
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let hash = script_hash.0;
|
||||
let (
|
||||
@@ -4527,7 +4592,7 @@ pub async fn run_job_by_hash_inner(
|
||||
_delete_after_use, // not taken into account in async endpoints
|
||||
timeout,
|
||||
has_preprocessor,
|
||||
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
|
||||
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
|
||||
check_scopes(&authed, || format!("run:script/{path}"))?;
|
||||
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
|
||||
cache_ttl = Some(run_query_cache_ttl);
|
||||
@@ -4536,7 +4601,7 @@ pub async fn run_job_by_hash_inner(
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
|
||||
@@ -82,6 +82,7 @@ pub mod smtp_server_ee;
|
||||
mod static_assets;
|
||||
mod stripe_ee;
|
||||
mod tracing_init;
|
||||
mod triggers;
|
||||
mod users;
|
||||
mod utils;
|
||||
mod variables;
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
schedule::clear_schedule,
|
||||
triggers::{
|
||||
get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail,
|
||||
},
|
||||
users::{maybe_refresh_folders, require_owner_of_path, AuthCache},
|
||||
utils::WithStarredInfoQuery,
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
@@ -53,7 +56,7 @@ use windmill_common::{
|
||||
utils::{
|
||||
not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath,
|
||||
},
|
||||
worker::{get_annotation_ts, to_raw_value},
|
||||
worker::to_raw_value,
|
||||
HUB_BASE_URL,
|
||||
};
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
@@ -132,6 +135,8 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/archive/p/*path", post(archive_script_by_path))
|
||||
.route("/get/draft/*path", get(get_script_by_path_w_draft))
|
||||
.route("/get/p/*path", get(get_script_by_path))
|
||||
.route("/get_triggers_count/*path", get(get_triggers_count))
|
||||
.route("/list_tokens/*path", get(list_tokens))
|
||||
.route("/raw/p/*path", get(raw_script_by_path))
|
||||
.route("/raw_unpinned/p/*path", get(raw_script_by_path_unpinned))
|
||||
.route("/exists/p/*path", get(exists_script_by_path))
|
||||
@@ -601,8 +606,8 @@ async fn create_script_internal<'c>(
|
||||
};
|
||||
|
||||
let lang = if &ns.language == &ScriptLang::Bun || &ns.language == &ScriptLang::Bunnative {
|
||||
let anns = get_annotation_ts(&ns.content);
|
||||
if anns.native_mode {
|
||||
let anns = windmill_common::worker::TypeScriptAnnotations::parse(&ns.content);
|
||||
if anns.native {
|
||||
ScriptLang::Bunnative
|
||||
} else {
|
||||
ScriptLang::Bun
|
||||
@@ -874,6 +879,22 @@ async fn get_script_by_path(
|
||||
Ok(Json(script))
|
||||
}
|
||||
|
||||
async fn list_tokens(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
|
||||
let path = path.to_path();
|
||||
list_tokens_internal(&db, &w_id, &path, false).await
|
||||
}
|
||||
|
||||
async fn get_triggers_count(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<TriggersCount> {
|
||||
let path = path.to_path();
|
||||
get_triggers_count_internal(&db, &w_id, &path, false).await
|
||||
}
|
||||
|
||||
async fn get_script_by_path_w_draft(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
use ::tracing::{field, Span};
|
||||
use hyper::Response;
|
||||
use tower_http::trace::{MakeSpan, OnFailure, OnResponse};
|
||||
use uuid::Uuid;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref LOG_REQUESTS: bool = std::env::var("LOG_REQUESTS")
|
||||
@@ -45,17 +46,28 @@ impl<B> OnFailure<B> for MyOnFailure {
|
||||
// tracing::error!(latency = latency.as_millis(), "response")
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref TRACING_HEADER: String = std::env::var("TRACING_HEADER")
|
||||
.ok().unwrap_or_else(|| "x-tracing-id".to_string());
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct MyMakeSpan {}
|
||||
|
||||
impl<B> MakeSpan<B> for MyMakeSpan {
|
||||
fn make_span(&mut self, request: &hyper::Request<B>) -> Span {
|
||||
let tracing_id = request
|
||||
.headers()
|
||||
.get(TRACING_HEADER.as_str())
|
||||
.and_then(|x| x.to_str().map(|x| x.to_string()).ok())
|
||||
.unwrap_or(Uuid::new_v4().to_string());
|
||||
tracing::info_span!(
|
||||
"request",
|
||||
method = %request.method(),
|
||||
uri = %request.uri(),
|
||||
username = field::Empty,
|
||||
workspace_id = field::Empty,
|
||||
trace_id = tracing_id,
|
||||
email = field::Empty,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
use axum::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
use windmill_common::error::JsonResult;
|
||||
|
||||
use crate::db::DB;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TriggerPrimarySchedule {
|
||||
schedule: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TriggersCount {
|
||||
primary_schedule: Option<TriggerPrimarySchedule>,
|
||||
schedule_count: i64,
|
||||
http_routes_count: i64,
|
||||
webhook_count: i64,
|
||||
email_count: i64,
|
||||
}
|
||||
pub(crate) async fn get_triggers_count_internal(
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
path: &str,
|
||||
is_flow: bool,
|
||||
) -> JsonResult<TriggersCount> {
|
||||
let primary_schedule = sqlx::query_scalar!(
|
||||
"SELECT schedule FROM schedule WHERE path = $1 AND script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
path,
|
||||
is_flow,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
|
||||
let schedule_count = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM schedule WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
path,
|
||||
is_flow,
|
||||
w_id
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let http_routes_count = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM http_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
path,
|
||||
is_flow,
|
||||
w_id
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let webhook_count = (if is_flow {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
|
||||
} else {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
}).fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let email_count = (if is_flow {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
|
||||
} else {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
}).fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(Json(TriggersCount {
|
||||
primary_schedule: primary_schedule.map(|s| TriggerPrimarySchedule { schedule: s }),
|
||||
schedule_count,
|
||||
http_routes_count,
|
||||
webhook_count,
|
||||
email_count,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
pub struct TruncatedTokenWithEmail {
|
||||
pub label: Option<String>,
|
||||
pub token_prefix: Option<String>,
|
||||
pub expiration: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub last_used_at: chrono::DateTime<chrono::Utc>,
|
||||
pub scopes: Option<Vec<String>>,
|
||||
pub email: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn list_tokens_internal(
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
path: &str,
|
||||
is_flow: bool,
|
||||
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
|
||||
let tokens = if is_flow {
|
||||
sqlx::query_as!(
|
||||
TruncatedTokenWithEmail,
|
||||
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
w_id, path).fetch_all(db)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as!(
|
||||
TruncatedTokenWithEmail,
|
||||
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
w_id, path)
|
||||
.fetch_all(db)
|
||||
.await?
|
||||
};
|
||||
Ok(Json(tokens))
|
||||
}
|
||||
@@ -57,6 +57,7 @@ futures-core.workspace = true
|
||||
async-stream.workspace = true
|
||||
const_format.workspace = true
|
||||
crc.workspace = true
|
||||
windmill-macros.workspace = true
|
||||
|
||||
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
tikv-jemalloc-ctl = { optional = true, workspace = true }
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::{
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use windmill_macros::annotations;
|
||||
|
||||
use crate::{error, global_settings::CUSTOM_TAGS_SETTING, server::Smtp, DB};
|
||||
|
||||
@@ -303,64 +304,25 @@ fn parse_file<T: FromStr>(path: &str) -> Option<T> {
|
||||
.flatten()
|
||||
}
|
||||
|
||||
#[annotations("#")]
|
||||
pub struct PythonAnnotations {
|
||||
pub no_cache: bool,
|
||||
pub no_uv: bool,
|
||||
}
|
||||
|
||||
#[annotations("//")]
|
||||
pub struct TypeScriptAnnotations {
|
||||
pub npm_mode: bool,
|
||||
pub nodejs_mode: bool,
|
||||
pub native_mode: bool,
|
||||
pub npm: bool,
|
||||
pub nodejs: bool,
|
||||
pub native: bool,
|
||||
pub nobundling: bool,
|
||||
}
|
||||
|
||||
pub fn get_annotation_ts(inner_content: &str) -> TypeScriptAnnotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("//"))
|
||||
.map(|x| x.to_string().replace("//", "").trim().to_string())
|
||||
.collect_vec();
|
||||
let nodejs_mode: bool = annotations.contains(&"nodejs".to_string());
|
||||
let npm_mode: bool = annotations.contains(&"npm".to_string());
|
||||
let native_mode: bool = annotations.contains(&"native".to_string());
|
||||
|
||||
//TODO: remove || npm_mode when bun build is more powerful
|
||||
let nobundling: bool =
|
||||
annotations.contains(&"nobundling".to_string()) || nodejs_mode || *DISABLE_BUNDLING;
|
||||
|
||||
TypeScriptAnnotations { npm_mode, nodejs_mode, native_mode, nobundling }
|
||||
}
|
||||
|
||||
pub struct PythonAnnotations {
|
||||
pub no_uv: bool,
|
||||
pub no_cache: bool,
|
||||
}
|
||||
|
||||
pub fn get_annotation_python(inner_content: &str) -> PythonAnnotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("#"))
|
||||
.map(|x| x.to_string().replace("#", "").trim().to_string())
|
||||
.collect_vec();
|
||||
|
||||
let no_uv: bool = annotations.contains(&"no_uv".to_string());
|
||||
let no_cache: bool = annotations.contains(&"no_cache".to_string());
|
||||
|
||||
PythonAnnotations { no_uv, no_cache }
|
||||
}
|
||||
|
||||
#[annotations("--")]
|
||||
pub struct SqlAnnotations {
|
||||
pub return_last_result: bool,
|
||||
}
|
||||
|
||||
pub fn get_sql_annotations(inner_content: &str) -> SqlAnnotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("--"))
|
||||
.map(|x| x.to_string().replace("--", "").trim().to_string())
|
||||
.collect_vec();
|
||||
|
||||
let return_last_result: bool = annotations.contains(&"return_last_result".to_string());
|
||||
|
||||
SqlAnnotations { return_last_result }
|
||||
}
|
||||
|
||||
pub async fn load_cache(bin_path: &str, _remote_path: &str) -> (bool, String) {
|
||||
if tokio::fs::metadata(&bin_path).await.is_ok() {
|
||||
(true, format!("loaded from local cache: {}\n", bin_path))
|
||||
|
||||
@@ -29,3 +29,4 @@ tempfile.workspace = true
|
||||
bytes.workspace = true
|
||||
object_store = { workspace = true, optional = true}
|
||||
tokio-tar.workspace = true
|
||||
lazy_static.workspace = true
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "windmill-macros"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2.workspace = true
|
||||
quote.workspace = true
|
||||
syn.workspace = true
|
||||
|
||||
# Dependencies for tests
|
||||
[dev-dependencies]
|
||||
# tests/annotation.rs
|
||||
lazy_static.workspace = true
|
||||
itertools.workspace = true
|
||||
regex.workspace = true
|
||||
@@ -0,0 +1,95 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Ident, ItemStruct, Lit};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn annotations(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as ItemStruct);
|
||||
let name = input.ident.clone();
|
||||
let fields = input
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| f.ident.clone().unwrap())
|
||||
.collect::<Vec<Ident>>();
|
||||
|
||||
// Match on the literal to extract the string value
|
||||
let comm_lit = match parse_macro_input!(attr as Lit) {
|
||||
Lit::Str(lit_str) => lit_str.value(), // This will give "#" without quotes
|
||||
_ => panic!("Expected a string literal"),
|
||||
};
|
||||
|
||||
// Generate regex
|
||||
let mut reg = format!("^{}|", &comm_lit);
|
||||
{
|
||||
for field in fields.iter() {
|
||||
reg.push_str(&(field.to_string()));
|
||||
reg.push_str("\\b");
|
||||
}
|
||||
|
||||
reg.push_str(r#"|\w+"#);
|
||||
}
|
||||
// Example of generated regex:
|
||||
// ^#
|
||||
// |ann1\b|ann2\b|ann3\b|ann4\b
|
||||
// |\w+
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#[derive(Default, Debug)]
|
||||
#input
|
||||
|
||||
impl std::ops::BitOrAssign for #name{
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
// Unfold fields
|
||||
// Read more: https://docs.rs/quote/latest/quote/macro.quote.html#interpolation
|
||||
#( self.#fields |= rhs.#fields; )*
|
||||
}
|
||||
}
|
||||
|
||||
impl #name {
|
||||
/// Autogenerated by windmill-macros
|
||||
pub fn parse(inner_content: &str) -> Self{
|
||||
let mut res = Self::default();
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE: regex::Regex = regex::Regex::new(#reg).unwrap();
|
||||
}
|
||||
// Create lines stream
|
||||
let mut lines = inner_content.lines();
|
||||
'outer: while let Some(line) = lines.next() {
|
||||
// If comment sign(s) on the right place
|
||||
let mut comms = false;
|
||||
// New instance
|
||||
// We will apply it if in line only annotations
|
||||
let mut new = Self::default();
|
||||
|
||||
'inner: for (i, mat) in RE.find_iter(line).enumerate() {
|
||||
|
||||
match mat.as_str(){
|
||||
#comm_lit if i == 0 => {
|
||||
comms = true;
|
||||
continue 'inner;
|
||||
},
|
||||
|
||||
// Will expand into something like:
|
||||
// "ann1" => new.ann1 = true,
|
||||
// "ann2" => new.ann2 = true,
|
||||
// "ann3" => new.ann3 = true,
|
||||
#( stringify!(#fields) => new.#fields = true, )*
|
||||
// Non annotations
|
||||
_ => continue 'outer,
|
||||
};
|
||||
}
|
||||
|
||||
if !comms {
|
||||
// We dont want to continue if line does not start with #
|
||||
return res;
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
res |= new;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#[cfg(test)]
|
||||
mod annotations_tests {
|
||||
|
||||
extern crate windmill_macros;
|
||||
use itertools::Itertools;
|
||||
use windmill_macros::annotations;
|
||||
|
||||
// Previous implementation.
|
||||
// We have to make sure that new one works the same as old one
|
||||
fn old(inner_content: &str) -> Annotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("#"))
|
||||
.map(|x| x.to_string().replace("#", "").trim().to_string())
|
||||
.collect_vec();
|
||||
|
||||
let ann1: bool = annotations.contains(&"ann1".to_string());
|
||||
let ann2: bool = annotations.contains(&"ann2".to_string());
|
||||
let ann3: bool = annotations.contains(&"ann3".to_string());
|
||||
let ann4: bool = annotations.contains(&"ann4".to_string());
|
||||
let ann5: bool = annotations.contains(&"ann5".to_string());
|
||||
|
||||
Annotations { ann1, ann2, ann3, ann4, ann5 }
|
||||
}
|
||||
|
||||
#[annotations("#")]
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
pub struct Annotations {
|
||||
pub ann1: bool,
|
||||
pub ann2: bool,
|
||||
pub ann3: bool,
|
||||
pub ann4: bool,
|
||||
pub ann5: bool,
|
||||
}
|
||||
|
||||
#[annotations("//")]
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
pub struct SlashedAnnotations {
|
||||
pub ann1: bool,
|
||||
pub ann2: bool,
|
||||
pub ann3: bool,
|
||||
pub ann4: bool,
|
||||
}
|
||||
|
||||
#[annotations("--")]
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
pub struct MinusedAnnotations {
|
||||
pub ann1: bool,
|
||||
pub ann2: bool,
|
||||
}
|
||||
|
||||
// e.g. rust, TS and JS
|
||||
#[test]
|
||||
fn slashed_annotations() {
|
||||
let cont = "// ann1
|
||||
// ann2
|
||||
//ann3";
|
||||
assert_eq!(
|
||||
SlashedAnnotations { ann1: true, ann2: true, ann3: true, ann4: false },
|
||||
SlashedAnnotations::parse(cont)
|
||||
);
|
||||
}
|
||||
|
||||
// e.g. Haskell, SQL
|
||||
#[test]
|
||||
fn minused_annotations() {
|
||||
let cont = "-- ann1
|
||||
-- ann2";
|
||||
assert_eq!(
|
||||
MinusedAnnotations { ann1: true, ann2: true },
|
||||
MinusedAnnotations::parse(cont)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_integration() {
|
||||
let cont = "# ann1";
|
||||
let expected = Annotations { ann1: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_integration() {
|
||||
let cont = "# ann2
|
||||
# ann3
|
||||
# ann4
|
||||
# ann5
|
||||
";
|
||||
let expected = Annotations {
|
||||
ann1: false,
|
||||
ann2: true,
|
||||
ann3: true,
|
||||
ann4: true,
|
||||
ann5: true,
|
||||
//
|
||||
};
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_integration() {
|
||||
// First line is ignored and not used
|
||||
{
|
||||
let cont = "
|
||||
# ann2";
|
||||
let expected = Annotations { ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
// Wrong spacing for ann3
|
||||
{
|
||||
let cont = "# ann2
|
||||
# ann3";
|
||||
|
||||
let expected = Annotations { ann2: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
// Drunk but valid spacing
|
||||
{
|
||||
let cont = "#ann1
|
||||
# ann2";
|
||||
let expected = Annotations { ann2: true, ann1: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comments_inbetween_integration() {
|
||||
let cont = "# ann2
|
||||
# Just comment, has nothing to do with annotations
|
||||
# Another comment: ann1 ann2 ann3
|
||||
# ann4 is not valid annotation
|
||||
# Actual annotation next line:
|
||||
# ann5
|
||||
|
||||
# Should be ignored
|
||||
# ann3
|
||||
";
|
||||
let expected = Annotations { ann2: true, ann5: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_collision() {
|
||||
// TODO
|
||||
}
|
||||
#[test]
|
||||
fn non_matching_integration() {
|
||||
{
|
||||
let cont = r#" "ann1", ann2 "#;
|
||||
let expected = Annotations::default();
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
// Empty
|
||||
{
|
||||
let cont = "";
|
||||
let expected = Annotations::default();
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,13 @@ mount {
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
dst: "/dev/shm"
|
||||
fstype: "tmpfs"
|
||||
rw: true
|
||||
is_bind: false
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "/dev/random"
|
||||
dst: "/dev/random"
|
||||
|
||||
@@ -5,7 +5,6 @@ use futures::{FutureExt, TryFutureExt};
|
||||
use serde_json::{json, value::RawValue, Value};
|
||||
use windmill_common::error::to_anyhow;
|
||||
use windmill_common::jobs::QueuedJob;
|
||||
use windmill_common::worker::get_sql_annotations;
|
||||
use windmill_common::{error::Error, worker::to_raw_value};
|
||||
use windmill_parser_sql::{
|
||||
parse_bigquery_sig, parse_db_resource, parse_sql_blocks, parse_sql_statement_named_params,
|
||||
@@ -238,7 +237,7 @@ pub async fn do_bigquery(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let service_account = CustomServiceAccount::from_json(&database)
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
|
||||
@@ -47,7 +47,7 @@ use windmill_common::{
|
||||
get_latest_hash_for_path,
|
||||
jobs::{QueuedJob, PREPROCESSOR_FAKE_ENTRYPOINT},
|
||||
scripts::ScriptLang,
|
||||
worker::{exists_in_cache, get_annotation_ts, save_cache, write_file},
|
||||
worker::{exists_in_cache, save_cache, write_file},
|
||||
DB,
|
||||
};
|
||||
|
||||
@@ -663,7 +663,7 @@ pub async fn prebundle_bun_script(
|
||||
if exists_in_cache(&local_path, &remote_path).await {
|
||||
return Ok(());
|
||||
}
|
||||
let annotation = get_annotation_ts(inner_content);
|
||||
let annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content);
|
||||
if annotation.nobundling {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -676,9 +676,9 @@ pub async fn prebundle_bun_script(
|
||||
&token,
|
||||
w_id,
|
||||
script_path,
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::NodeBundle
|
||||
} else if annotation.native_mode {
|
||||
} else if annotation.native {
|
||||
LoaderMode::BrowserBundle
|
||||
} else {
|
||||
LoaderMode::BunBundle
|
||||
@@ -800,7 +800,7 @@ pub async fn handle_bun_job(
|
||||
new_args: &mut Option<HashMap<String, Box<RawValue>>>,
|
||||
occupancy_metrics: &mut OccupancyMetrics,
|
||||
) -> error::Result<Box<RawValue>> {
|
||||
let mut annotation = windmill_common::worker::get_annotation_ts(inner_content);
|
||||
let mut annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content);
|
||||
|
||||
let (mut has_bundle_cache, cache_logs, local_path, remote_path) =
|
||||
if requirements_o.is_some() && !annotation.nobundling && codebase.is_none() {
|
||||
@@ -822,7 +822,7 @@ pub async fn handle_bun_job(
|
||||
|
||||
if !codebase.is_some() && !has_bundle_cache {
|
||||
let _ = write_file(job_dir, "main.ts", inner_content)?;
|
||||
} else if !annotation.native_mode && codebase.is_none() {
|
||||
} else if !annotation.native && codebase.is_none() {
|
||||
let _ = write_file(job_dir, "package.json", r#"{ "type": "module" }"#)?;
|
||||
};
|
||||
|
||||
@@ -830,7 +830,7 @@ pub async fn handle_bun_job(
|
||||
get_common_bun_proc_envs(Some(&base_internal_url)).await;
|
||||
|
||||
if codebase.is_some() {
|
||||
annotation.nodejs_mode = true
|
||||
annotation.nodejs = true
|
||||
}
|
||||
let (main_override, apply_preprocessor) = match get_main_override(job.args.as_ref()) {
|
||||
Some(main_override) => {
|
||||
@@ -844,7 +844,7 @@ pub async fn handle_bun_job(
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if annotation.nodejs_mode || annotation.npm_mode {
|
||||
if annotation.nodejs || annotation.npm {
|
||||
return Err(error::Error::ExecutionErr(
|
||||
"Nodejs / npm mode is an EE feature".to_string(),
|
||||
));
|
||||
@@ -875,20 +875,20 @@ pub async fn handle_bun_job(
|
||||
pull_codebase(&job.workspace_id, codebase, job_dir).await?;
|
||||
} else if let Some(reqs) = requirements_o.as_ref() {
|
||||
let splitted = reqs.split(BUN_LOCKB_SPLIT).collect::<Vec<&str>>();
|
||||
if splitted.len() != 2 && !annotation.npm_mode {
|
||||
if splitted.len() != 2 && !annotation.npm {
|
||||
return Err(error::Error::ExecutionErr(
|
||||
format!("Invalid requirements, expected to find //bun.lockb split pattern in reqs. Found: |{reqs}|")
|
||||
));
|
||||
}
|
||||
|
||||
let _ = write_file(job_dir, "package.json", &splitted[0])?;
|
||||
let lockb = if annotation.npm_mode { "" } else { splitted[1] };
|
||||
let lockb = if annotation.npm { "" } else { splitted[1] };
|
||||
if lockb != EMPTY_FILE {
|
||||
let mut skip_install = false;
|
||||
let mut create_buntar = false;
|
||||
let mut buntar_path = "".to_string();
|
||||
|
||||
if !annotation.npm_mode {
|
||||
if !annotation.npm {
|
||||
let _ = write_lockb(&splitted[1], job_dir).await?;
|
||||
|
||||
let mut sha_path = sha2::Sha256::new();
|
||||
@@ -921,7 +921,7 @@ pub async fn handle_bun_job(
|
||||
job_dir,
|
||||
worker_name,
|
||||
common_bun_proc_envs.clone(),
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut Some(occupancy_metrics),
|
||||
)
|
||||
.await?;
|
||||
@@ -963,7 +963,7 @@ pub async fn handle_bun_job(
|
||||
worker_name,
|
||||
false,
|
||||
None,
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut Some(occupancy_metrics),
|
||||
)
|
||||
.await?;
|
||||
@@ -971,19 +971,19 @@ pub async fn handle_bun_job(
|
||||
// }
|
||||
}
|
||||
|
||||
let mut init_logs = if annotation.native_mode {
|
||||
let mut init_logs = if annotation.native {
|
||||
"\n\n--- NATIVE CODE EXECUTION ---\n".to_string()
|
||||
} else if has_bundle_cache {
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
"\n\n--- NODE BUNDLE SNAPSHOT EXECUTION ---\n".to_string()
|
||||
} else {
|
||||
"\n\n--- BUN BUNDLE SNAPSHOT EXECUTION ---\n".to_string()
|
||||
}
|
||||
} else if codebase.is_some() {
|
||||
"\n\n--- NODE CODEBASE SNAPSHOT EXECUTION ---\n".to_string()
|
||||
} else if annotation.native_mode {
|
||||
} else if annotation.native {
|
||||
"\n\n--- NATIVE CODE EXECUTION ---\n".to_string()
|
||||
} else if annotation.nodejs_mode {
|
||||
} else if annotation.nodejs {
|
||||
write_file(job_dir, "main.ts", &remove_pinned_imports(inner_content)?)?;
|
||||
"\n\n--- NODE CODE EXECUTION ---\n".to_string()
|
||||
} else {
|
||||
@@ -1003,7 +1003,7 @@ pub async fn handle_bun_job(
|
||||
}
|
||||
|
||||
let write_wrapper_f = async {
|
||||
if !has_bundle_cache && annotation.native_mode {
|
||||
if !has_bundle_cache && annotation.native {
|
||||
return Ok(()) as error::Result<()>;
|
||||
}
|
||||
// let mut start = Instant::now();
|
||||
@@ -1105,6 +1105,16 @@ try {{
|
||||
if (step_id) {{
|
||||
err["step_id"] = step_id;
|
||||
}}
|
||||
const extra = {{}};
|
||||
Object.getOwnPropertyNames(e).forEach((key) => {{
|
||||
if (['line', 'name', 'stack', 'column', 'message', 'sourceURL', 'originalLine', 'originalColumn'].includes(key)) {{
|
||||
return;
|
||||
}}
|
||||
extra[key] = e[key];
|
||||
}});
|
||||
if (Object.keys(extra).length > 0) {{
|
||||
err["extra"] = extra;
|
||||
}}
|
||||
await fs.writeFile("result.json", JSON.stringify(err));
|
||||
process.exit(1);
|
||||
}}
|
||||
@@ -1116,7 +1126,7 @@ try {{
|
||||
|
||||
let reserved_variables_args_out_f = async {
|
||||
let args_and_out_f = async {
|
||||
if !annotation.native_mode {
|
||||
if !annotation.native {
|
||||
create_args_and_out_file(&client, job, job_dir, db).await?;
|
||||
}
|
||||
Ok(()) as Result<()>
|
||||
@@ -1133,7 +1143,7 @@ try {{
|
||||
let build_cache = !has_bundle_cache
|
||||
&& !annotation.nobundling
|
||||
&& !codebase.is_some()
|
||||
&& (requirements_o.is_some() || annotation.native_mode);
|
||||
&& (requirements_o.is_some() || annotation.native);
|
||||
|
||||
let write_loader_f = async {
|
||||
if build_cache {
|
||||
@@ -1143,9 +1153,9 @@ try {{
|
||||
&client.get_token().await,
|
||||
&job.workspace_id,
|
||||
&job.script_path(),
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::NodeBundle
|
||||
} else if annotation.native_mode {
|
||||
} else if annotation.native {
|
||||
LoaderMode::BrowserBundle
|
||||
} else {
|
||||
LoaderMode::BunBundle
|
||||
@@ -1161,7 +1171,7 @@ try {{
|
||||
&client.get_token().await,
|
||||
&job.workspace_id,
|
||||
&job.script_path(),
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::Node
|
||||
} else {
|
||||
LoaderMode::Bun
|
||||
@@ -1207,7 +1217,7 @@ try {{
|
||||
}
|
||||
}
|
||||
}
|
||||
if !annotation.native_mode {
|
||||
if !annotation.native {
|
||||
let ex_wrapper = read_file_content(&format!("{job_dir}/wrapper.mjs")).await?;
|
||||
write_file(
|
||||
job_dir,
|
||||
@@ -1221,7 +1231,7 @@ try {{
|
||||
}
|
||||
fs::remove_file(format!("{job_dir}/main.ts"))?;
|
||||
has_bundle_cache = true;
|
||||
} else if annotation.nodejs_mode {
|
||||
} else if annotation.nodejs {
|
||||
generate_wrapper_mjs(
|
||||
job_dir,
|
||||
&job.workspace_id,
|
||||
@@ -1237,9 +1247,14 @@ try {{
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
if annotation.native_mode {
|
||||
if annotation.native {
|
||||
#[cfg(not(feature = "deno_core"))]
|
||||
return Ok(to_raw_value("").unwrap());
|
||||
{
|
||||
tracing::error!(
|
||||
r#""deno_core" feature is not activated, but "//native" annotation used. Returning empty value..."#
|
||||
);
|
||||
return Ok(to_raw_value("").unwrap());
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
{
|
||||
@@ -1299,14 +1314,7 @@ try {{
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
&NSJAIL_CONFIG_RUN_BUN_CONTENT
|
||||
.replace(
|
||||
"{LANG}",
|
||||
if annotation.nodejs_mode {
|
||||
"nodejs"
|
||||
} else {
|
||||
"bun"
|
||||
},
|
||||
)
|
||||
.replace("{LANG}", if annotation.nodejs { "nodejs" } else { "bun" })
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CACHE_DIR}", BUN_CACHE_DIR)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
@@ -1314,7 +1322,7 @@ try {{
|
||||
"{SHARED_MOUNT}",
|
||||
&shared_mount.replace(
|
||||
"/tmp/shared",
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
"/tmp/nodejs/shared"
|
||||
} else {
|
||||
"/tmp/bun/shared"
|
||||
@@ -1324,7 +1332,7 @@ try {{
|
||||
)?;
|
||||
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
let args = if annotation.nodejs_mode {
|
||||
let args = if annotation.nodejs {
|
||||
vec![
|
||||
"--config",
|
||||
"run.config.proto",
|
||||
@@ -1368,7 +1376,7 @@ try {{
|
||||
.stderr(Stdio::piped());
|
||||
start_child_process(nsjail_cmd, NSJAIL_PATH.as_str()).await?
|
||||
} else {
|
||||
let cmd = if annotation.nodejs_mode {
|
||||
let cmd = if annotation.nodejs {
|
||||
let script_path = format!("{job_dir}/wrapper.mjs");
|
||||
|
||||
let mut bun_cmd = Command::new(&*NODE_BIN_PATH);
|
||||
@@ -1420,7 +1428,7 @@ try {{
|
||||
|
||||
start_child_process(
|
||||
cmd,
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
&*NODE_BIN_PATH
|
||||
} else {
|
||||
&*BUN_PATH
|
||||
@@ -1435,7 +1443,7 @@ try {{
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
child,
|
||||
false,
|
||||
!*DISABLE_NSJAIL,
|
||||
worker_name,
|
||||
&job.workspace_id,
|
||||
"bun run",
|
||||
@@ -1525,10 +1533,10 @@ pub async fn start_worker(
|
||||
let common_bun_proc_envs: HashMap<String, String> =
|
||||
get_common_bun_proc_envs(Some(&base_internal_url)).await;
|
||||
|
||||
let mut annotation = windmill_common::worker::get_annotation_ts(inner_content);
|
||||
let mut annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content);
|
||||
|
||||
//TODO: remove this when bun dedicated workers work without issues
|
||||
annotation.nodejs_mode = true;
|
||||
annotation.nodejs = true;
|
||||
|
||||
let context = variables::get_reserved_variables(
|
||||
db,
|
||||
@@ -1582,7 +1590,7 @@ pub async fn start_worker(
|
||||
job_dir,
|
||||
worker_name,
|
||||
common_bun_proc_envs.clone(),
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut None,
|
||||
)
|
||||
.await?;
|
||||
@@ -1603,7 +1611,7 @@ pub async fn start_worker(
|
||||
worker_name,
|
||||
false,
|
||||
None,
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut None,
|
||||
)
|
||||
.await?;
|
||||
@@ -1681,7 +1689,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
|
||||
token,
|
||||
w_id,
|
||||
script_path,
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::Node
|
||||
} else {
|
||||
LoaderMode::Bun
|
||||
@@ -1690,7 +1698,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
|
||||
.await?;
|
||||
}
|
||||
|
||||
if annotation.nodejs_mode && !codebase.is_some() {
|
||||
if annotation.nodejs && !codebase.is_some() {
|
||||
generate_wrapper_mjs(
|
||||
job_dir,
|
||||
w_id,
|
||||
@@ -1706,7 +1714,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
|
||||
.await?;
|
||||
}
|
||||
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
let script_path = format!("{job_dir}/wrapper.mjs");
|
||||
|
||||
handle_dedicated_process(
|
||||
|
||||
@@ -126,6 +126,7 @@ pub async fn generate_deno_lock(
|
||||
"--unstable-http",
|
||||
"--lock=lock.json",
|
||||
"--frozen=false",
|
||||
"--allow-import",
|
||||
"--import-map",
|
||||
&import_map_path,
|
||||
"main.ts",
|
||||
@@ -372,6 +373,7 @@ try {{
|
||||
args.push(allow_read.as_str());
|
||||
args.push("--allow-write=./");
|
||||
args.push("--allow-env");
|
||||
args.push("--allow-import");
|
||||
args.push("--allow-run=git,/usr/bin/chromium");
|
||||
} else {
|
||||
args.push("-A");
|
||||
|
||||
@@ -414,9 +414,30 @@ async fn get_mem_peak(pid: Option<u32>, nsjail: bool) -> i32 {
|
||||
return -1;
|
||||
}
|
||||
let pid = if nsjail {
|
||||
// This is a bit hacky, but the process id of the nsjail process is the pid of nsjail + 1.
|
||||
// Ideally, we would get the number from fork() itself. This works in MOST cases.
|
||||
pid.unwrap() + 1
|
||||
// Read /proc/<nsjail_pid>/task/<nsjail_pid>/children and extract pid
|
||||
let nsjail_pid = pid.unwrap();
|
||||
let children_path = format!("/proc/{}/task/{}/children", nsjail_pid, nsjail_pid);
|
||||
if let Ok(mut file) = File::open(children_path).await {
|
||||
let mut contents = String::new();
|
||||
if tokio::io::AsyncReadExt::read_to_string(&mut file, &mut contents)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
if let Some(child_pid) = contents.split_whitespace().next() {
|
||||
if let Ok(child_pid) = child_pid.parse::<u32>() {
|
||||
child_pid
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
pid.unwrap()
|
||||
};
|
||||
|
||||
@@ -247,7 +247,7 @@ pub async fn eval_timeout(
|
||||
#[cfg(not(feature = "deno_core"))]
|
||||
{
|
||||
#[allow(unreachable_code)]
|
||||
return todo!();
|
||||
return Err(anyhow::anyhow!("Deno core is not enabled".to_string()).into());
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
|
||||
@@ -9,7 +9,7 @@ use tokio::net::TcpStream;
|
||||
use tokio_util::compat::TokioAsyncWriteCompatExt;
|
||||
use uuid::Uuid;
|
||||
use windmill_common::error::{self, Error};
|
||||
use windmill_common::worker::{get_sql_annotations, to_raw_value};
|
||||
use windmill_common::worker::to_raw_value;
|
||||
use windmill_common::{error::to_anyhow, jobs::QueuedJob};
|
||||
use windmill_parser_sql::{parse_db_resource, parse_mssql_sig};
|
||||
use windmill_queue::{append_logs, CanceledBy};
|
||||
@@ -68,7 +68,7 @@ pub async fn do_mssql(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let mut config = Config::new();
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use tokio::sync::Mutex;
|
||||
use windmill_common::{
|
||||
error::{to_anyhow, Error},
|
||||
jobs::QueuedJob,
|
||||
worker::{get_sql_annotations, to_raw_value},
|
||||
worker::to_raw_value,
|
||||
};
|
||||
use windmill_parser_sql::{
|
||||
parse_db_resource, parse_mysql_sig, parse_sql_blocks, parse_sql_statement_named_params,
|
||||
@@ -148,7 +148,7 @@ pub async fn do_mysql(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let opts = OptsBuilder::default()
|
||||
.db_name(Some(database.database))
|
||||
|
||||
@@ -30,7 +30,7 @@ use tokio_postgres::{
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use windmill_common::error::{self, Error};
|
||||
use windmill_common::worker::{get_sql_annotations, to_raw_value, CLOUD_HOSTED};
|
||||
use windmill_common::worker::{to_raw_value, CLOUD_HOSTED};
|
||||
use windmill_common::{error::to_anyhow, jobs::QueuedJob};
|
||||
use windmill_parser::{Arg, Typ};
|
||||
use windmill_parser_sql::{
|
||||
@@ -191,7 +191,7 @@ pub async fn do_postgresql(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let sslmode = match database.sslmode.as_deref() {
|
||||
Some("allow") => "prefer".to_string(),
|
||||
|
||||
@@ -345,7 +345,7 @@ try {{
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
child,
|
||||
false,
|
||||
!*DISABLE_NSJAIL,
|
||||
worker_name,
|
||||
&job.workspace_id,
|
||||
"php run",
|
||||
|
||||
@@ -498,7 +498,10 @@ except BaseException as e:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
tb = traceback.format_tb(exc_traceback)
|
||||
with open(result_json, 'w') as f:
|
||||
err = {{ "message": str(e), "name": e.__class__.__name__, "stack": '\n'.join(tb[1:]) }}
|
||||
err = {{ "message": str(e), "name": e.__class__.__name__, "stack": '\n'.join(tb[1:]) }}
|
||||
extra = e.__dict__
|
||||
if extra and len(extra) > 0:
|
||||
err['extra'] = extra
|
||||
flow_node_id = os.environ.get('WM_FLOW_STEP_ID')
|
||||
if flow_node_id:
|
||||
err['step_id'] = flow_node_id
|
||||
@@ -895,7 +898,7 @@ async fn handle_python_deps(
|
||||
let requirements = match requirements_o {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
let annotation = windmill_common::worker::get_annotation_python(inner_content);
|
||||
let annotation = windmill_common::worker::PythonAnnotations::parse(inner_content);
|
||||
let mut already_visited = vec![];
|
||||
|
||||
let requirements = windmill_parser_py_imports::parse_python_imports(
|
||||
|
||||
@@ -9,7 +9,6 @@ use serde_json::{json, value::RawValue, Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::HashMap;
|
||||
use windmill_common::error::to_anyhow;
|
||||
use windmill_common::worker::get_sql_annotations;
|
||||
|
||||
use windmill_common::jobs::QueuedJob;
|
||||
use windmill_common::{error::Error, worker::to_raw_value};
|
||||
@@ -266,7 +265,7 @@ pub async fn do_snowflake(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let qualified_username = format!(
|
||||
"{}.{}",
|
||||
|
||||
@@ -12,7 +12,7 @@ use windmill_common::flows::{FlowModule, FlowModuleValue};
|
||||
use windmill_common::get_latest_deployed_hash_for_path;
|
||||
use windmill_common::jobs::JobPayload;
|
||||
use windmill_common::scripts::ScriptHash;
|
||||
use windmill_common::worker::{get_annotation_ts, to_raw_value, to_raw_value_owned, write_file};
|
||||
use windmill_common::worker::{to_raw_value, to_raw_value_owned, write_file};
|
||||
use windmill_common::{
|
||||
error::{self, to_anyhow},
|
||||
flows::FlowValue,
|
||||
@@ -953,10 +953,10 @@ async fn lock_modules<'c>(
|
||||
}
|
||||
|
||||
if language == ScriptLang::Bun || language == ScriptLang::Bunnative {
|
||||
let anns = get_annotation_ts(&content);
|
||||
if anns.native_mode && language == ScriptLang::Bun {
|
||||
let anns = windmill_common::worker::TypeScriptAnnotations::parse(&content);
|
||||
if anns.native && language == ScriptLang::Bun {
|
||||
language = ScriptLang::Bunnative;
|
||||
} else if !anns.native_mode && language == ScriptLang::Bunnative {
|
||||
} else if !anns.native && language == ScriptLang::Bunnative {
|
||||
language = ScriptLang::Bun;
|
||||
};
|
||||
}
|
||||
@@ -1003,10 +1003,10 @@ async fn lock_modules<'c>(
|
||||
|
||||
fn skip_creating_new_lock(language: &ScriptLang, content: &str) -> bool {
|
||||
if language == &ScriptLang::Bun || language == &ScriptLang::Bunnative {
|
||||
let anns = get_annotation_ts(&content);
|
||||
if anns.native_mode && language == &ScriptLang::Bun {
|
||||
let anns = windmill_common::worker::TypeScriptAnnotations::parse(&content);
|
||||
if anns.native && language == &ScriptLang::Bun {
|
||||
return false;
|
||||
} else if !anns.native_mode && language == &ScriptLang::Bunnative {
|
||||
} else if !anns.native && language == &ScriptLang::Bunnative {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -1077,11 +1077,13 @@ async fn lock_modules_app(
|
||||
match new_lock {
|
||||
Ok(new_lock) => {
|
||||
append_logs(&job.id, &job.workspace_id, logs, db).await;
|
||||
let anns = get_annotation_ts(&content);
|
||||
let nlang = if anns.native_mode && language == ScriptLang::Bun {
|
||||
let anns =
|
||||
windmill_common::worker::TypeScriptAnnotations::parse(
|
||||
&content,
|
||||
);
|
||||
let nlang = if anns.native && language == ScriptLang::Bun {
|
||||
Some(ScriptLang::Bunnative)
|
||||
} else if !anns.native_mode && language == ScriptLang::Bunnative
|
||||
{
|
||||
} else if !anns.native && language == ScriptLang::Bunnative {
|
||||
Some(ScriptLang::Bun)
|
||||
} else {
|
||||
None
|
||||
@@ -1437,7 +1439,7 @@ async fn capture_dependency_job(
|
||||
}
|
||||
ScriptLang::Bun | ScriptLang::Bunnative => {
|
||||
let npm_mode = npm_mode.unwrap_or_else(|| {
|
||||
windmill_common::worker::get_annotation_ts(job_raw_code).npm_mode
|
||||
windmill_common::worker::TypeScriptAnnotations::parse(job_raw_code).npm
|
||||
});
|
||||
if !raw_deps {
|
||||
let _ = write_file(job_dir, "main.ts", job_raw_code)?;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
|
||||
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
|
||||
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
|
||||
|
||||
export const VERSION = "v1.407.1";
|
||||
export const VERSION = "v1.409.2";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { requireLogin, resolveWorkspace, validatePath } from "./context.ts";
|
||||
import { colors, Command, log, SEP, Table, yamlParse } from "./deps.ts";
|
||||
import { colors, Command, log, SEP, Table, yamlParseFile } from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
import { ListableApp, Policy } from "./gen/types.gen.ts";
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function pushApp(
|
||||
return;
|
||||
}
|
||||
alreadySynced.push(localPath);
|
||||
remotePath.replaceAll(SEP, "/");
|
||||
remotePath = remotePath.replaceAll(SEP, "/");
|
||||
let app: any = undefined;
|
||||
// deleting old app if it exists in raw mode
|
||||
try {
|
||||
@@ -40,8 +40,8 @@ export async function pushApp(
|
||||
if (!localPath.endsWith(SEP)) {
|
||||
localPath += SEP;
|
||||
}
|
||||
const localAppRaw = await Deno.readTextFile(localPath + "app.yaml");
|
||||
const localApp = yamlParse(localAppRaw) as AppFile;
|
||||
const path = localPath + "app.yaml";
|
||||
const localApp = (await yamlParseFile(path)) as AppFile;
|
||||
|
||||
function replaceInlineScripts(rec: any) {
|
||||
if (!rec) {
|
||||
|
||||
+2
-4
@@ -1,4 +1,4 @@
|
||||
import { log, yamlParse } from "./deps.ts";
|
||||
import { log, yamlParseFile } from "./deps.ts";
|
||||
|
||||
export interface SyncOptions {
|
||||
stateful?: boolean;
|
||||
@@ -40,9 +40,7 @@ export interface Codebase {
|
||||
|
||||
export async function readConfigFile(): Promise<SyncOptions> {
|
||||
try {
|
||||
const conf = yamlParse(
|
||||
await Deno.readTextFile("wmill.yaml")
|
||||
) as SyncOptions;
|
||||
const conf = (await yamlParseFile("wmill.yaml")) as SyncOptions;
|
||||
if (conf?.defaultTs == undefined) {
|
||||
log.warn(
|
||||
"No defaultTs defined in your wmill.yaml. Using 'bun' as default."
|
||||
|
||||
+23
-1
@@ -21,7 +21,29 @@ export { copy } from "jsr:@std/io/copy";
|
||||
export { readAll } from "jsr:@std/io/read-all";
|
||||
|
||||
export * as log from "jsr:@std/log";
|
||||
export { stringify as yamlStringify, parse as yamlParse } from "jsr:@std/yaml";
|
||||
export { stringify as yamlStringify } from "jsr:@std/yaml";
|
||||
|
||||
import { parse as yamlParse, ParseOptions } from "jsr:@std/yaml";
|
||||
|
||||
export async function yamlParseFile(path: string, options: ParseOptions = {}) {
|
||||
try {
|
||||
return yamlParse(await Deno.readTextFile(path), options);
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing yaml ${path}`, { cause: e });
|
||||
}
|
||||
}
|
||||
|
||||
export function yamlParseContent(
|
||||
path: string,
|
||||
content: string,
|
||||
options: ParseOptions = {}
|
||||
) {
|
||||
try {
|
||||
return yamlParse(content, options);
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing yaml ${path}`, { cause: e });
|
||||
}
|
||||
}
|
||||
|
||||
// other
|
||||
|
||||
|
||||
+20
-18
@@ -1,7 +1,7 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { GlobalOptions, isSuperset } from "./types.ts";
|
||||
import { Confirm, SEP, log, yamlStringify } from "./deps.ts";
|
||||
import { colors, Command, Table, yamlParse } from "./deps.ts";
|
||||
import { colors, Command, Table, yamlParseFile } from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
|
||||
import { requireLogin, resolveWorkspace, validatePath } from "./context.ts";
|
||||
@@ -29,21 +29,23 @@ export function replaceInlineScripts(
|
||||
) {
|
||||
modules.forEach((m) => {
|
||||
if (m.value.type == "rawscript") {
|
||||
const path = m.value.content.split(" ")[1];
|
||||
m.value.content = Deno.readTextFileSync(localPath + path);
|
||||
const lock = m.value.lock;
|
||||
if (removeLocks && removeLocks.includes(path)) {
|
||||
m.value.lock = undefined;
|
||||
} else if (
|
||||
lock &&
|
||||
typeof lock == "string" &&
|
||||
lock.trimStart().startsWith("!inline ")
|
||||
) {
|
||||
const path = lock.split(" ")[1];
|
||||
try {
|
||||
m.value.lock = readInlinePathSync(localPath + path);
|
||||
} catch {
|
||||
log.error(`Lock file ${path} not found`);
|
||||
if (m.value.content.startsWith("!inline")) {
|
||||
const path = m.value.content.split(" ")[1];
|
||||
m.value.content = Deno.readTextFileSync(localPath + path);
|
||||
const lock = m.value.lock;
|
||||
if (removeLocks && removeLocks.includes(path)) {
|
||||
m.value.lock = undefined;
|
||||
} else if (
|
||||
lock &&
|
||||
typeof lock == "string" &&
|
||||
lock.trimStart().startsWith("!inline ")
|
||||
) {
|
||||
const path = lock.split(" ")[1];
|
||||
try {
|
||||
m.value.lock = readInlinePathSync(localPath + path);
|
||||
} catch {
|
||||
log.error(`Lock file ${path} not found`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m.value.type == "forloopflow") {
|
||||
@@ -87,8 +89,7 @@ export async function pushFlow(
|
||||
if (!localPath.endsWith(SEP)) {
|
||||
localPath += SEP;
|
||||
}
|
||||
const localFlowRaw = await Deno.readTextFile(localPath + "flow.yaml");
|
||||
const localFlow = yamlParse(localFlowRaw) as FlowFile;
|
||||
const localFlow = (await yamlParseFile(localPath + "flow.yaml")) as FlowFile;
|
||||
|
||||
replaceInlineScripts(localFlow.value.modules, localPath, undefined);
|
||||
|
||||
@@ -120,6 +121,7 @@ export async function pushFlow(
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
//@ts-ignore
|
||||
`Failed to create flow ${remotePath}: ${e.body ?? e.message}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export async function pushFolder(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -87,6 +88,7 @@ export async function pushFolder(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
throw Error(`Failed to create folder ${name}: ${e.body ?? e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export const OpenAPI: OpenAPIConfig = {
|
||||
PASSWORD: undefined,
|
||||
TOKEN: getEnv("WM_TOKEN"),
|
||||
USERNAME: undefined,
|
||||
VERSION: '1.401.0',
|
||||
VERSION: '1.407.2',
|
||||
WITH_CREDENTIALS: true,
|
||||
interceptors: {
|
||||
request: new Interceptors(),
|
||||
|
||||
@@ -1015,6 +1015,9 @@ export type FlowModule = {
|
||||
skip_if_stopped?: boolean;
|
||||
expr: string;
|
||||
};
|
||||
skip_if?: {
|
||||
expr: string;
|
||||
};
|
||||
sleep?: InputTransform;
|
||||
cache_ttl?: number;
|
||||
timeout?: number;
|
||||
@@ -1176,6 +1179,7 @@ export type FlowStatusModule = {
|
||||
approver: string;
|
||||
}>;
|
||||
failed_retries?: Array<(string)>;
|
||||
skipped?: boolean;
|
||||
};
|
||||
|
||||
export type type4 = 'WaitingForPriorSteps' | 'WaitingForEvents' | 'WaitingForExecutor' | 'InProgress' | 'Success' | 'Failure';
|
||||
|
||||
+6
-5
@@ -3,7 +3,7 @@ import {
|
||||
path,
|
||||
Confirm,
|
||||
yamlStringify,
|
||||
yamlParse,
|
||||
yamlParseFile,
|
||||
Command,
|
||||
setClient,
|
||||
Table,
|
||||
@@ -459,9 +459,9 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
}
|
||||
|
||||
try {
|
||||
const workspaceSettings = yamlParse(
|
||||
await Deno.readTextFile("settings.yaml")
|
||||
) as SimplifiedSettings;
|
||||
const workspaceSettings = (await yamlParseFile(
|
||||
"settings.yaml"
|
||||
)) as SimplifiedSettings;
|
||||
await workspaceSetup(
|
||||
{
|
||||
token: instance.token,
|
||||
@@ -578,6 +578,7 @@ async function whoami(opts: {}) {
|
||||
log.info(JSON.stringify(whoamiInfo, null, 2));
|
||||
} catch (error) {
|
||||
log.error(
|
||||
//@ts-ignore
|
||||
colors.red(`Failed to retrieve whoami information: ${error.message}`)
|
||||
);
|
||||
}
|
||||
@@ -620,7 +621,7 @@ const command = new Command()
|
||||
.description("Remove an instance")
|
||||
.complete("instance", async () => (await allInstances()).map((x) => x.name))
|
||||
.arguments("<instance:string:instance>")
|
||||
.action(async (instance) => {
|
||||
.action(async (instance: any) => {
|
||||
const instances = await allInstances();
|
||||
|
||||
const choice = (await Select.prompt({
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.407.1";
|
||||
export const VERSION = "1.409.2";
|
||||
|
||||
const command = new Command()
|
||||
.name("wmill")
|
||||
|
||||
+16
-10
@@ -1,6 +1,13 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
import { SEP, colors, log, path, yamlParse, yamlStringify } from "./deps.ts";
|
||||
import {
|
||||
SEP,
|
||||
colors,
|
||||
log,
|
||||
path,
|
||||
yamlParseFile,
|
||||
yamlStringify,
|
||||
} from "./deps.ts";
|
||||
import {
|
||||
ScriptMetadata,
|
||||
defaultScriptMetadata,
|
||||
@@ -119,9 +126,9 @@ export async function generateFlowLockInternal(
|
||||
return remote_path;
|
||||
}
|
||||
|
||||
const flowValue = yamlParse(
|
||||
await Deno.readTextFile(folder! + SEP + "flow.yaml")
|
||||
) as FlowFile;
|
||||
const flowValue = (await yamlParseFile(
|
||||
folder! + SEP + "flow.yaml"
|
||||
)) as FlowFile;
|
||||
|
||||
if (!justUpdateMetadataLock) {
|
||||
const changedScripts = [];
|
||||
@@ -798,7 +805,7 @@ export async function parseMetadataFile(
|
||||
try {
|
||||
metadataFilePath = scriptPath + ".script.yaml";
|
||||
await Deno.stat(metadataFilePath);
|
||||
const payload: any = yamlParse(await Deno.readTextFile(metadataFilePath));
|
||||
const payload: any = await yamlParseFile(metadataFilePath);
|
||||
replaceLock(payload);
|
||||
|
||||
return {
|
||||
@@ -840,9 +847,9 @@ export async function parseMetadataFile(
|
||||
codebases,
|
||||
false
|
||||
);
|
||||
scriptInitialMetadata = yamlParse(
|
||||
await Deno.readTextFile(metadataFilePath)
|
||||
) as ScriptMetadata;
|
||||
scriptInitialMetadata = (await yamlParseFile(
|
||||
metadataFilePath
|
||||
)) as ScriptMetadata;
|
||||
replaceLock(scriptInitialMetadata);
|
||||
} catch (e) {
|
||||
log.info(
|
||||
@@ -868,8 +875,7 @@ interface Lock {
|
||||
const WMILL_LOCKFILE = "wmill-lock.yaml";
|
||||
export async function readLockfile(): Promise<Lock> {
|
||||
try {
|
||||
const lockfile = await Deno.readTextFile(WMILL_LOCKFILE);
|
||||
const read = yamlParse(lockfile);
|
||||
const read = await yamlParseFile(WMILL_LOCKFILE);
|
||||
if (typeof read == "object") {
|
||||
return read as Lock;
|
||||
} else {
|
||||
|
||||
+32
-21
@@ -1,7 +1,7 @@
|
||||
import { yamlStringify } from "./deps.ts";
|
||||
import { Confirm } from "./deps.ts";
|
||||
import { colors } from "./deps.ts";
|
||||
import { yamlParse } from "./deps.ts";
|
||||
import { yamlParseFile } from "./deps.ts";
|
||||
import { log } from "./deps.ts";
|
||||
import { compareInstanceObjects } from "./instance.ts";
|
||||
import { isSuperset } from "./types.ts";
|
||||
@@ -259,17 +259,24 @@ export async function pushWorkspaceKey(
|
||||
}
|
||||
}
|
||||
|
||||
export async function readInstanceSettings() {
|
||||
let localSettings: GlobalSetting[] = [];
|
||||
|
||||
try {
|
||||
localSettings = (await yamlParseFile(
|
||||
"instance_settings.yaml"
|
||||
)) as GlobalSetting[];
|
||||
} catch {
|
||||
log.warn("No instance_settings.yaml found");
|
||||
}
|
||||
return localSettings;
|
||||
}
|
||||
|
||||
export async function pullInstanceSettings(preview = false) {
|
||||
const remoteSettings = await wmill.listGlobalSettings();
|
||||
|
||||
if (preview) {
|
||||
let localSettings: GlobalSetting[] = [];
|
||||
|
||||
try {
|
||||
localSettings = yamlParse(
|
||||
await Deno.readTextFile("instance_settings.yaml")
|
||||
) as GlobalSetting[];
|
||||
} catch {}
|
||||
const localSettings: GlobalSetting[] = await readInstanceSettings();
|
||||
|
||||
return compareInstanceObjects(
|
||||
remoteSettings,
|
||||
@@ -294,9 +301,7 @@ export async function pushInstanceSettings(
|
||||
baseUrl?: string
|
||||
) {
|
||||
const remoteSettings = await wmill.listGlobalSettings();
|
||||
let localSettings = (await Deno.readTextFile("instance_settings.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as GlobalSetting[];
|
||||
let localSettings: GlobalSetting[] = await readInstanceSettings();
|
||||
|
||||
if (baseUrl) {
|
||||
localSettings = localSettings.filter((s) => s.name !== "base_url");
|
||||
@@ -354,6 +359,17 @@ export async function pushInstanceSettings(
|
||||
}
|
||||
}
|
||||
|
||||
export async function readLocalConfigs() {
|
||||
let localConfigs: Config[] = [];
|
||||
|
||||
try {
|
||||
localConfigs = (await yamlParseFile("instance_configs.yaml")) as Config[];
|
||||
} catch {
|
||||
log.warn("No instance_configs.yaml found");
|
||||
}
|
||||
return localConfigs;
|
||||
}
|
||||
|
||||
export async function pullInstanceConfigs(preview = false) {
|
||||
const remoteConfigs = (await wmill.listConfigs()).map((x) => {
|
||||
return {
|
||||
@@ -363,12 +379,7 @@ export async function pullInstanceConfigs(preview = false) {
|
||||
});
|
||||
|
||||
if (preview) {
|
||||
let localConfigs: Config[] = [];
|
||||
try {
|
||||
localConfigs = yamlParse(
|
||||
await Deno.readTextFile("instance_configs.yaml")
|
||||
) as Config[];
|
||||
} catch {}
|
||||
const localConfigs: Config[] = await readLocalConfigs();
|
||||
|
||||
return compareInstanceObjects(
|
||||
remoteConfigs,
|
||||
@@ -395,9 +406,7 @@ export async function pushInstanceConfigs(preview: boolean = false) {
|
||||
name: removeWorkerPrefix(x.name),
|
||||
};
|
||||
});
|
||||
const localConfigs = (await Deno.readTextFile("instance_configs.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as Config[];
|
||||
const localConfigs = await readLocalConfigs();
|
||||
|
||||
if (preview) {
|
||||
return compareInstanceObjects(
|
||||
@@ -415,7 +424,9 @@ export async function pushInstanceConfigs(preview: boolean = false) {
|
||||
}
|
||||
try {
|
||||
await wmill.updateConfig({
|
||||
name: config.name.startsWith('worker__') ? config.name : `worker__${config.name}`,
|
||||
name: config.name.startsWith("worker__")
|
||||
? config.name
|
||||
: `worker__${config.name}`,
|
||||
requestBody: config.config,
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
+6
-6
@@ -9,7 +9,7 @@ import {
|
||||
path,
|
||||
log,
|
||||
yamlStringify,
|
||||
yamlParse,
|
||||
yamlParseContent,
|
||||
SEP,
|
||||
} from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
@@ -112,7 +112,7 @@ async function addCodebaseDigestIfRelevant(
|
||||
if (isTs) {
|
||||
const c = findCodebase(path, codebases);
|
||||
if (c) {
|
||||
const parsed: any = yamlParse(content);
|
||||
const parsed: any = yamlParseContent(path, content);
|
||||
if (parsed && typeof parsed == "object") {
|
||||
parsed["codebase"] = c.digest;
|
||||
parsed["lock"] = undefined;
|
||||
@@ -660,7 +660,7 @@ export async function elementsToMap(
|
||||
if (json) {
|
||||
o = JSON.parse(content);
|
||||
} else {
|
||||
o = yamlParse(content);
|
||||
o = yamlParseContent(path, content);
|
||||
}
|
||||
if (o["is_secret"]) {
|
||||
continue;
|
||||
@@ -704,7 +704,7 @@ async function compareDynFSElement(
|
||||
|
||||
function parseYaml(k: string, v: string) {
|
||||
if (k.endsWith(".script.yaml")) {
|
||||
const o: any = yamlParse(v);
|
||||
const o: any = yamlParseContent(k, v);
|
||||
if (typeof o == "object") {
|
||||
if (Array.isArray(o?.["lock"])) {
|
||||
o["lock"] = o["lock"].join("\n");
|
||||
@@ -715,7 +715,7 @@ async function compareDynFSElement(
|
||||
}
|
||||
return o;
|
||||
} else if (k.endsWith(".app.yaml")) {
|
||||
const o: any = yamlParse(v);
|
||||
const o: any = yamlParseContent(k, v);
|
||||
const o2 = o["policy"];
|
||||
|
||||
if (typeof o2 == "object") {
|
||||
@@ -728,7 +728,7 @@ async function compareDynFSElement(
|
||||
}
|
||||
return o;
|
||||
} else {
|
||||
return yamlParse(v);
|
||||
return yamlParseContent(k, v);
|
||||
}
|
||||
}
|
||||
for (const [k, v] of Object.entries(m1)) {
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ import {
|
||||
colors,
|
||||
log,
|
||||
path,
|
||||
yamlParse,
|
||||
yamlParseContent,
|
||||
yamlStringify,
|
||||
} from "./deps.ts";
|
||||
import { pushApp } from "./apps.ts";
|
||||
@@ -115,7 +115,7 @@ export async function pushObj(
|
||||
newObj: any,
|
||||
plainSecrets: boolean,
|
||||
alreadySynced: string[],
|
||||
message?: string,
|
||||
message?: string
|
||||
) {
|
||||
const typeEnding = getTypeStrFromPath(p);
|
||||
|
||||
@@ -155,7 +155,7 @@ export async function pushObj(
|
||||
|
||||
export function parseFromPath(p: string, content: string): any {
|
||||
return p.endsWith(".yaml")
|
||||
? yamlParse(content)
|
||||
? yamlParseContent(p, content)
|
||||
: p.endsWith(".json")
|
||||
? JSON.parse(content)
|
||||
: content;
|
||||
@@ -164,7 +164,7 @@ export function parseFromFile(p: string): any {
|
||||
if (p.endsWith(".json")) {
|
||||
return JSON.parse(Deno.readTextFileSync(p));
|
||||
} else if (p.endsWith(".yaml") || p.endsWith(".yml")) {
|
||||
return yamlParse(Deno.readTextFileSync(p));
|
||||
return yamlParseContent(p, Deno.readTextFileSync(p));
|
||||
} else {
|
||||
throw new Error("Could not read file " + p);
|
||||
}
|
||||
@@ -227,7 +227,7 @@ export function getTypeStrFromPath(
|
||||
return typeEnding;
|
||||
} else {
|
||||
if (isFileResource(p)) {
|
||||
return "resource"
|
||||
return "resource";
|
||||
}
|
||||
throw new Error("Could not infer type of path " + JSON.stringify(parsed));
|
||||
}
|
||||
|
||||
+34
-17
@@ -13,7 +13,7 @@ import {
|
||||
log,
|
||||
Table,
|
||||
yamlStringify,
|
||||
yamlParse,
|
||||
yamlParseFile,
|
||||
} from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
import {
|
||||
@@ -170,6 +170,7 @@ export async function pushWorkspaceUser(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -189,6 +190,7 @@ export async function pushWorkspaceUser(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -251,6 +253,7 @@ export async function pushGroup(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -301,6 +304,7 @@ export async function pushGroup(
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -329,6 +333,7 @@ export async function pushGroup(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -368,11 +373,13 @@ export async function pushGroup(
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -383,11 +390,7 @@ export async function pullInstanceUsers(preview: boolean = false) {
|
||||
const remoteUsers = await wmill.globalUsersExport();
|
||||
|
||||
if (preview) {
|
||||
let localUsers: ExportedUser[] = [];
|
||||
try {
|
||||
const raw = await Deno.readTextFile("instance_users.yaml");
|
||||
localUsers = yamlParse(raw) as ExportedUser[];
|
||||
} catch {}
|
||||
const localUsers: ExportedUser[] = await readInstanceUsers();
|
||||
return compareInstanceObjects(remoteUsers, localUsers, "email", "user");
|
||||
} else {
|
||||
log.info("Pulling users from instance...");
|
||||
@@ -399,11 +402,31 @@ export async function pullInstanceUsers(preview: boolean = false) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function readInstanceUsers() {
|
||||
let localUsers: ExportedUser[] = [];
|
||||
try {
|
||||
localUsers = (await yamlParseFile("instance_users.yaml")) as ExportedUser[];
|
||||
} catch {
|
||||
log.warn("No instance_users.yaml file found");
|
||||
}
|
||||
return localUsers;
|
||||
}
|
||||
|
||||
export async function readInstanceGroups() {
|
||||
let localGroups: InstanceGroup[] = [];
|
||||
try {
|
||||
localGroups = (await yamlParseFile(
|
||||
"instance_groups.yaml"
|
||||
)) as ExportedInstanceGroup[];
|
||||
} catch {
|
||||
log.warn("No instance_groups.yaml file found");
|
||||
}
|
||||
return localGroups;
|
||||
}
|
||||
|
||||
export async function pushInstanceUsers(preview: boolean = false) {
|
||||
const remoteUsers = await wmill.globalUsersExport();
|
||||
const localUsers = (await Deno.readTextFile("instance_users.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as ExportedUser[];
|
||||
const localUsers: ExportedUser[] = await readInstanceUsers();
|
||||
|
||||
if (preview) {
|
||||
return compareInstanceObjects(localUsers, remoteUsers, "email", "user");
|
||||
@@ -421,11 +444,7 @@ export async function pullInstanceGroups(preview = false) {
|
||||
const remoteGroups = await wmill.exportInstanceGroups();
|
||||
|
||||
if (preview) {
|
||||
let localGroups: InstanceGroup[] = [];
|
||||
try {
|
||||
const raw = await Deno.readTextFile("instance_groups.yaml");
|
||||
localGroups = yamlParse(raw) as InstanceGroup[];
|
||||
} catch {}
|
||||
const localGroups = await readInstanceGroups();
|
||||
return compareInstanceObjects(remoteGroups, localGroups, "name", "group");
|
||||
} else {
|
||||
log.info("Pulling groups from instance...");
|
||||
@@ -441,9 +460,7 @@ export async function pullInstanceGroups(preview = false) {
|
||||
|
||||
export async function pushInstanceGroups(preview: boolean = false) {
|
||||
const remoteGroups = await wmill.exportInstanceGroups();
|
||||
const localGroups = (await Deno.readTextFile("instance_groups.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as ExportedInstanceGroup[];
|
||||
const localGroups = await readInstanceGroups();
|
||||
|
||||
if (preview) {
|
||||
return compareInstanceObjects(localGroups, remoteGroups, "name", "group");
|
||||
|
||||
+4
-1
@@ -55,7 +55,10 @@ export function deepEqual<T>(a: T, b: T): boolean {
|
||||
if (a.valueOf !== Object.prototype.valueOf) {
|
||||
return a.valueOf() === b.valueOf();
|
||||
}
|
||||
if (a.toString !== Object.prototype.toString) {
|
||||
if (
|
||||
a.toString !== Object.prototype.toString &&
|
||||
typeof a.toString == "function"
|
||||
) {
|
||||
return a.toString() === b.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@ ARG PYTHON_IMAGE=python:3.11.10-slim-bookworm
|
||||
|
||||
FROM ${RUST_IMAGE} AS rust_base
|
||||
|
||||
RUN yum install -y rust-toolset
|
||||
|
||||
RUN yum update -y && \
|
||||
yum install -y git openssl-devel npm nodejs rustfmt
|
||||
|
||||
# Install rust manually
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo install cargo-chef --version ^0.1
|
||||
|
||||
WORKDIR /windmill
|
||||
@@ -35,7 +37,7 @@ COPY /backend/parsers/windmill-parser-wasm/pkg/ /backend/parsers/windmill-parser
|
||||
COPY /typescript-client/docs/ /frontend/static/tsdocs/
|
||||
|
||||
RUN npm run generate-backend-client
|
||||
ENV NODE_OPTIONS "--max-old-space-size=8192"
|
||||
ENV NODE_OPTIONS "--max-old-space-size=10240"
|
||||
RUN npm run build
|
||||
|
||||
|
||||
|
||||
+10
-11
@@ -3,17 +3,16 @@
|
||||
This directory contains the Dockerfiles for building Windmill binaries for Red Hat Linux 9.
|
||||
|
||||
We build Windmill on the Red Hat Universal Base Image 9. Windmill requires the xmlsec1-devel package which is not available in the default UBI9 repositories. It is however included in the CodeReady Builder for RHEL9 repository which requires a RedHat subscription.
|
||||
Moreover, only rust v1.75 is supported on Red Hat Linux 9. To make Windmill compatible with Rust v1.75, you need to pin the following libraries:
|
||||
```
|
||||
aws-config = "=1.4.0"
|
||||
aws-sdk-sts = "=1.25.0"
|
||||
aws-sdk-ssooidc = "=1.25.0"
|
||||
aws-sdk-sso = "=1.25.0"
|
||||
```
|
||||
|
||||
Make sure to include `aws-sdk-ssooidc` and `aws-sdk-sso` in the Cargo.toml of windmill-common as well to enforce the correct versions of the nested dependencies. Make them optional and include them in the `parquet` feature.
|
||||
It's also possible that you need to add `#[async_recursion]` to the `lock_modules` function in the `backend/windmill-worker/src/worker_lockfiles.rs` file for it to compile.
|
||||
|
||||
Once the image is built, you can simply copy the binary on any Red Hat Linux 9 machine and run it. You will just need to install the xmlsec1 package which can be installed directly using `yum/dnf install xmlsec1`.
|
||||
|
||||
|
||||
## Notes
|
||||
- you will need to register on Red Hat and have an individual developer subscription and pass the username and password to docker build:
|
||||
```
|
||||
docker build \
|
||||
-f docker/RHEL9/Dockerfile \
|
||||
--build-arg features="$features" \
|
||||
--secret id=rh_username,src=/path/to/rh_username \
|
||||
--secret id=rh_password,src=/path/to/rh_password \
|
||||
.
|
||||
```
|
||||
@@ -1,28 +0,0 @@
|
||||
Error parsing /git/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (8350:3)
|
||||
|
||||
8347 | application/json:
|
||||
8348 | schema: {}
|
||||
8349 |
|
||||
8350 | /w/{workspace}/job_helpers/loa ...
|
||||
----------^
|
||||
8351 | get:
|
||||
8352 | summary: Load a preview of ...
|
||||
ParserError: Error parsing /git/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (8350:3)
|
||||
|
||||
8347 | application/json:
|
||||
8348 | schema: {}
|
||||
8349 |
|
||||
8350 | /w/{workspace}/job_helpers/loa ...
|
||||
----------^
|
||||
8351 | get:
|
||||
8352 | summary: Load a preview of ...
|
||||
at Object.parse (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23)
|
||||
at getResult (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22)
|
||||
at runNextPlugin (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32)
|
||||
at /git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9
|
||||
at new Promise (<anonymous>)
|
||||
at Object.run (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12)
|
||||
at parseFile (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38)
|
||||
at parse (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30)
|
||||
at async $RefParser.parse (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28)
|
||||
at async $RefParser.resolve (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13)
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.407.1",
|
||||
"version": "1.409.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.407.1",
|
||||
"version": "1.409.2",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.407.1",
|
||||
"version": "1.409.2",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -141,6 +141,9 @@
|
||||
defaultValue?: any,
|
||||
nnullable?: boolean
|
||||
) {
|
||||
if (label == 'toString' && typeof value == 'function') {
|
||||
value = undefined
|
||||
}
|
||||
if ((value == undefined || value == null) && !ignoreValueUndefined) {
|
||||
value = defaultValue
|
||||
if (defaultValue === undefined || defaultValue === null) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import AssignableTagsInner from './AssignableTagsInner.svelte'
|
||||
|
||||
export let placement: 'bottom-end' | 'top-end' = 'bottom-end'
|
||||
export let color: 'nord' | 'dark' = 'dark'
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
@@ -13,7 +14,7 @@
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true}>
|
||||
<Button {color} size="xs" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row gap-1 items-center"
|
||||
><Pen size={14} /> Custom Tags <Tooltip light
|
||||
>Tags are assigned to scripts and flows. Workers only accept jobs that correspond to their
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
>
|
||||
<input type="text" placeholder="yourorg" bind:value={org} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
||||
<input type="text" placeholder="Custom Name" bind:value={value['display_name']} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Client Id</span>
|
||||
<input type="text" placeholder="Client Id" bind:value={value['id']} />
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
>
|
||||
<input type="text" placeholder="yourorg" bind:value={org} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
||||
<input type="text" placeholder="Custom Name" bind:value={value['display_name']} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Client Id</span>
|
||||
<input type="text" placeholder="Client Id" bind:value={value['id']} />
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
|
||||
<div class="w-full flex space-x-8">
|
||||
<div class="w-full flex flex-col gap-4">
|
||||
<Label label="Cron">
|
||||
<Label label="Cron" class="font-semibold" primary={true}>
|
||||
<svelte:fragment slot="error">
|
||||
{#if !validCRON}
|
||||
<div class="text-red-600 text-xs"> Invalid cron syntax </div>
|
||||
@@ -219,10 +219,10 @@
|
||||
{disabled}
|
||||
/>
|
||||
</Label>
|
||||
<Label label="Timezone">
|
||||
<Label label="Timezone" class="font-semibold" primary>
|
||||
{#if disabled}
|
||||
<div>
|
||||
<Badge>{timezone}</Badge>
|
||||
<Badge><span class="text-primary dark:text-primary-inverse">{timezone}</span></Badge>
|
||||
</div>
|
||||
{:else}
|
||||
<Select
|
||||
@@ -390,9 +390,8 @@
|
||||
</div>
|
||||
|
||||
<div class="w-full flex flex-col space-y-2">
|
||||
<div class="text-sm font-semibold leading-none">Execution summary</div>
|
||||
<div class="text-sm font-semibold leading-none">Estimated upcoming events ({timezone})</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div class="text-sm">Estimated upcoming events ({timezone})</div>
|
||||
<div class="flex flex-col rounded-md p-4 border text-tertiary bg-surface-secondary gap-0.5">
|
||||
{#each preview as date}
|
||||
<span class="text-sm">{dateFormatter(new Date(date))}</span>
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
WorkspaceService,
|
||||
type InputTransform,
|
||||
type RawScript,
|
||||
type PathScript
|
||||
type PathScript,
|
||||
type TriggersCount
|
||||
} from '$lib/gen'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { copilotInfo, userStore, workspaceStore } from '$lib/stores'
|
||||
@@ -41,14 +42,13 @@
|
||||
import { workspacedOpenai } from './copilot/lib'
|
||||
import type { FlowCopilotContext, FlowCopilotModule } from './copilot/flow'
|
||||
import { pickScript } from './flows/flowStateUtils'
|
||||
import type { Schedule } from './flows/scheduleUtils'
|
||||
import {
|
||||
approximateFindPythonRelativePath,
|
||||
isTypescriptRelativePath,
|
||||
parseTypescriptDeps
|
||||
} from '$lib/relative_imports'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
|
||||
import type { ScheduleTrigger, TriggerContext } from './triggers'
|
||||
$: token = $page.url.searchParams.get('wm_token') ?? undefined
|
||||
$: workspace = $page.url.searchParams.get('workspace') ?? undefined
|
||||
$: themeDarkRaw = $page.url.searchParams.get('activeColorTheme')
|
||||
@@ -467,13 +467,7 @@
|
||||
}
|
||||
|
||||
const flowStateStore = writable({} as FlowState)
|
||||
const scheduleStore = writable<Schedule>({
|
||||
args: {},
|
||||
cron: '',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
enabled: false,
|
||||
summary: undefined
|
||||
})
|
||||
|
||||
const previewArgsStore = writable<Record<string, any>>({})
|
||||
const scriptEditorDrawer = writable(undefined)
|
||||
const moving = writable<{ module: FlowModule; modules: FlowModule[] } | undefined>(undefined)
|
||||
@@ -481,10 +475,19 @@
|
||||
|
||||
const testStepStore = writable<Record<string, any>>({})
|
||||
const selectedIdStore = writable('settings-metadata')
|
||||
const selectedTriggerStore = writable<'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes'>(
|
||||
'webhooks'
|
||||
)
|
||||
|
||||
const primaryScheduleStore = writable<ScheduleTrigger | undefined | false>(undefined)
|
||||
const triggersCount = writable<TriggersCount | undefined>(undefined)
|
||||
setContext<TriggerContext>('TriggerContext', {
|
||||
primarySchedule: primaryScheduleStore,
|
||||
selectedTrigger: selectedTriggerStore,
|
||||
triggersCount: triggersCount
|
||||
})
|
||||
setContext<FlowEditorContext>('FlowEditorContext', {
|
||||
selectedId: selectedIdStore,
|
||||
schedule: scheduleStore,
|
||||
previewArgs: previewArgsStore,
|
||||
scriptEditorDrawer,
|
||||
moving,
|
||||
|
||||
@@ -559,6 +559,11 @@
|
||||
<pre class="text-xs pt-2 whitespace-pre-wrap text-primary"
|
||||
>{result.error.stack ?? ''}</pre
|
||||
>
|
||||
{#if result.error?.extra}
|
||||
<pre class="text-xs pt-2 whitespace-pre-wrap text-primary"
|
||||
>{JSON.stringify(result.error.extra, null, 4)}</pre
|
||||
>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
{#if language === 'bun'}
|
||||
@@ -627,7 +632,7 @@
|
||||
>
|
||||
</button>
|
||||
{:else if !result?.disable_download}
|
||||
<FileDownload s3object={result} />
|
||||
<FileDownload {workspaceId} s3object={result} />
|
||||
<button
|
||||
class="text-secondary underline text-2xs whitespace-nowrap"
|
||||
on:click={() => {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
ScriptService,
|
||||
type OpenFlow,
|
||||
type RawScript,
|
||||
type InputTransform
|
||||
type InputTransform,
|
||||
type TriggersCount
|
||||
} from '$lib/gen'
|
||||
import { initHistory, push, redo, undo } from '$lib/history'
|
||||
import {
|
||||
@@ -40,7 +41,6 @@
|
||||
import { dfs, getPreviousIds } from './flows/previousResults'
|
||||
import FlowImportExportMenu from './flows/header/FlowImportExportMenu.svelte'
|
||||
import FlowPreviewButtons from './flows/header/FlowPreviewButtons.svelte'
|
||||
import { loadFlowSchedule, type Schedule } from './flows/scheduleUtils'
|
||||
import type { FlowEditorContext, FlowInput } from './flows/types'
|
||||
import { cleanInputs, emptyFlowModuleState } from './flows/utils'
|
||||
import {
|
||||
@@ -82,6 +82,7 @@
|
||||
import Summary from './Summary.svelte'
|
||||
import type { FlowBuilderWhitelabelCustomUi } from './custom_ui'
|
||||
import FlowYamlEditor from './flows/header/FlowYamlEditor.svelte'
|
||||
import { type TriggerContext, type ScheduleTrigger } from './triggers'
|
||||
|
||||
export let initialPath: string = ''
|
||||
export let pathStoreInit: string | undefined = undefined
|
||||
@@ -100,30 +101,47 @@
|
||||
export let customUi: FlowBuilderWhitelabelCustomUi = {}
|
||||
export let disableAi: boolean = false
|
||||
export let disabledFlowInputs = false
|
||||
export let savedPrimarySchedule: ScheduleTrigger | undefined = undefined
|
||||
|
||||
$: setContext('customUi', customUi)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function createSchedule(path: string) {
|
||||
const { cron, timezone, args, enabled, summary } = $scheduleStore
|
||||
const primaryScheduleStore = writable<ScheduleTrigger | undefined | false>(savedPrimarySchedule)
|
||||
const triggersCount = writable<TriggersCount | undefined>(
|
||||
savedPrimarySchedule
|
||||
? { schedule_count: 1, primary_schedule: { schedule: savedPrimarySchedule.cron } }
|
||||
: undefined
|
||||
)
|
||||
|
||||
try {
|
||||
await ScheduleService.createSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path: path,
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
script_path: path,
|
||||
is_flow: true,
|
||||
args,
|
||||
enabled,
|
||||
summary
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
sendUserToast(`The primary schedule could not be created: ${err}`, true)
|
||||
export function setPrimarySchedule(schedule: ScheduleTrigger | undefined | false) {
|
||||
primaryScheduleStore.set(schedule)
|
||||
loadTriggers()
|
||||
}
|
||||
|
||||
async function createSchedule(path: string) {
|
||||
if ($primaryScheduleStore) {
|
||||
const { cron, timezone, args, enabled, summary } = $primaryScheduleStore
|
||||
|
||||
try {
|
||||
await ScheduleService.createSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path: path,
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
script_path: path,
|
||||
is_flow: true,
|
||||
args,
|
||||
enabled,
|
||||
summary
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
sendUserToast(`The primary schedule could not be created: ${err}`, true)
|
||||
}
|
||||
} else {
|
||||
sendUserToast('The primary schedule could not be created: no schedule data', true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +205,8 @@
|
||||
typ: 'flow',
|
||||
value: {
|
||||
...flow,
|
||||
path: $pathStore
|
||||
path: $pathStore,
|
||||
primary_schedule: $primaryScheduleStore
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -241,7 +260,7 @@
|
||||
// console.log('flow', computeUnlockedSteps(flow)) // del
|
||||
// loadingSave = false // del
|
||||
// return
|
||||
const { cron, timezone, args, enabled, summary } = $scheduleStore
|
||||
|
||||
if (newFlow) {
|
||||
try {
|
||||
localStorage.removeItem('flow')
|
||||
@@ -264,7 +283,7 @@
|
||||
deployment_message: deploymentMsg || undefined
|
||||
}
|
||||
})
|
||||
if (enabled) {
|
||||
if ($primaryScheduleStore && $primaryScheduleStore.enabled) {
|
||||
await createSchedule($pathStore)
|
||||
}
|
||||
} else {
|
||||
@@ -284,31 +303,40 @@
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath
|
||||
})
|
||||
if (
|
||||
JSON.stringify(schedule.args) != JSON.stringify(args) ||
|
||||
schedule.schedule != cron ||
|
||||
schedule.timezone != timezone ||
|
||||
schedule.summary != summary
|
||||
) {
|
||||
await ScheduleService.updateSchedule({
|
||||
if ($primaryScheduleStore) {
|
||||
const { cron, timezone, args, enabled, summary } = $primaryScheduleStore
|
||||
|
||||
if (
|
||||
JSON.stringify(schedule.args) != JSON.stringify(args) ||
|
||||
schedule.schedule != cron ||
|
||||
schedule.timezone != timezone ||
|
||||
schedule.summary != summary
|
||||
) {
|
||||
await ScheduleService.updateSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
requestBody: {
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
args,
|
||||
summary
|
||||
}
|
||||
})
|
||||
}
|
||||
if (enabled != schedule.enabled) {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
requestBody: { enabled }
|
||||
})
|
||||
}
|
||||
} else if (scheduleExists) {
|
||||
await ScheduleService.deleteSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
requestBody: {
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
args,
|
||||
summary
|
||||
}
|
||||
path: $pathStore
|
||||
})
|
||||
}
|
||||
if (enabled != schedule.enabled) {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
requestBody: { enabled }
|
||||
})
|
||||
}
|
||||
} else if (enabled) {
|
||||
} else if ($primaryScheduleStore && $primaryScheduleStore.enabled) {
|
||||
await createSchedule(initialPath)
|
||||
}
|
||||
|
||||
@@ -359,7 +387,8 @@
|
||||
encodeState({
|
||||
flow: $flowStore,
|
||||
path: $pathStore,
|
||||
selectedId: $selectedIdStore
|
||||
selectedId: $selectedIdStore,
|
||||
primarySchedule: $primaryScheduleStore
|
||||
})
|
||||
)
|
||||
} catch (err) {
|
||||
@@ -369,18 +398,14 @@
|
||||
}
|
||||
|
||||
const selectedIdStore = writable<string>(selectedId ?? 'settings-metadata')
|
||||
const selectedTriggerStore = writable<'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes'>(
|
||||
'webhooks'
|
||||
)
|
||||
|
||||
export function getSelectedId() {
|
||||
return $selectedIdStore
|
||||
}
|
||||
|
||||
const scheduleStore = writable<Schedule>({
|
||||
summary: undefined,
|
||||
args: {},
|
||||
cron: '',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
enabled: false
|
||||
})
|
||||
const previewArgsStore = writable<Record<string, any>>(initialArgs)
|
||||
const scriptEditorDrawer = writable<ScriptEditorDrawer | undefined>(undefined)
|
||||
const moving = writable<{ module: FlowModule; modules: FlowModule[] } | undefined>(undefined)
|
||||
@@ -395,10 +420,14 @@
|
||||
selectedIdStore.set(selectedId)
|
||||
}
|
||||
|
||||
function selectTrigger(selectedTrigger: 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes') {
|
||||
selectedTriggerStore.set(selectedTrigger)
|
||||
}
|
||||
|
||||
let insertButtonOpen = writable<boolean>(false)
|
||||
|
||||
setContext<FlowEditorContext>('FlowEditorContext', {
|
||||
selectedId: selectedIdStore,
|
||||
schedule: scheduleStore,
|
||||
previewArgs: previewArgsStore,
|
||||
scriptEditorDrawer,
|
||||
moving,
|
||||
@@ -414,25 +443,31 @@
|
||||
insertButtonOpen
|
||||
})
|
||||
|
||||
async function loadSchedule() {
|
||||
loadFlowSchedule(initialPath, $workspaceStore!)
|
||||
.then((schedule: Schedule) => {
|
||||
scheduleStore.set(schedule)
|
||||
})
|
||||
.catch(() => {
|
||||
scheduleStore.set({
|
||||
summary: undefined,
|
||||
cron: '0 */5 * * *',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
args: {},
|
||||
enabled: false
|
||||
})
|
||||
})
|
||||
setContext<TriggerContext>('TriggerContext', {
|
||||
selectedTrigger: selectedTriggerStore,
|
||||
primarySchedule: primaryScheduleStore,
|
||||
triggersCount
|
||||
})
|
||||
|
||||
async function loadTriggers() {
|
||||
$triggersCount = await FlowService.getTriggersCountOfFlow({
|
||||
workspace: $workspaceStore!,
|
||||
path: initialPath
|
||||
})
|
||||
if ($primaryScheduleStore && $triggersCount.primary_schedule == undefined) {
|
||||
$triggersCount = {
|
||||
...($triggersCount ?? {}),
|
||||
schedule_count: ($triggersCount.schedule_count ?? 0) + 1,
|
||||
primary_schedule: {
|
||||
schedule: $primaryScheduleStore.cron
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: selectedId && select(selectedId)
|
||||
|
||||
$: initialPath && initialPath != '' && $workspaceStore && loadSchedule()
|
||||
$: initialPath && initialPath != '' && $workspaceStore && loadTriggers()
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
let classes = event.target?.['className']
|
||||
@@ -699,10 +734,13 @@
|
||||
}
|
||||
|
||||
if (module.type === 'trigger') {
|
||||
if (!$scheduleStore.cron) {
|
||||
$scheduleStore.cron = '0 */15 * * *'
|
||||
$primaryScheduleStore = {
|
||||
summary: 'Scheduled poll of flow',
|
||||
args: {},
|
||||
cron: '0 */15 * * *',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
enabled: true
|
||||
}
|
||||
$scheduleStore.enabled = true
|
||||
}
|
||||
|
||||
const flowModule: FlowModule & {
|
||||
@@ -1152,7 +1190,7 @@
|
||||
</div>
|
||||
|
||||
<div class="gap-4 flex-row hidden md:flex w-full max-w-md">
|
||||
{#if $scheduleStore.enabled}
|
||||
{#if $primaryScheduleStore != undefined ? $primaryScheduleStore && $primaryScheduleStore?.enabled : $triggersCount?.primary_schedule}
|
||||
<Button
|
||||
btnClasses="hidden lg:inline-flex"
|
||||
startIcon={{ icon: Calendar }}
|
||||
@@ -1160,10 +1198,15 @@
|
||||
color="light"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
select('settings-schedule')
|
||||
select('triggers')
|
||||
selectTrigger('schedules')
|
||||
}}
|
||||
>
|
||||
{$scheduleStore.cron ?? ''}
|
||||
{$primaryScheduleStore != undefined
|
||||
? $primaryScheduleStore
|
||||
? $primaryScheduleStore?.cron
|
||||
: ''
|
||||
: $triggersCount?.primary_schedule?.schedule}
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
@@ -1334,6 +1377,7 @@
|
||||
on:reload={() => {
|
||||
renderCount += 1
|
||||
}}
|
||||
{newFlow}
|
||||
/>
|
||||
{:else}
|
||||
<CenteredPage>Loading...</CenteredPage>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
description?: string
|
||||
value: FlowValue
|
||||
schema?: any
|
||||
path?: string
|
||||
}
|
||||
|
||||
export let overflowAuto = false
|
||||
@@ -32,20 +33,25 @@
|
||||
class:overflow-auto={overflowAuto}
|
||||
>
|
||||
<FlowGraphV2
|
||||
path={flow?.path}
|
||||
{download}
|
||||
minHeight={400}
|
||||
modules={flow?.value?.modules}
|
||||
failureModule={flow?.value?.failure_module}
|
||||
preprocessorModule={flow?.value?.preprocessor_module}
|
||||
on:select={(e) => {
|
||||
if (e?.detail === 'failure') {
|
||||
let nodeId = e?.detail
|
||||
if (nodeId === 'triggers') {
|
||||
dispatch('triggerDetail')
|
||||
return
|
||||
} else if (nodeId === 'failure') {
|
||||
stepDetail = flow?.value?.failure_module
|
||||
} else if (e?.detail === 'preprocessor') {
|
||||
} else if (nodeId === 'preprocessor') {
|
||||
stepDetail = flow?.value?.preprocessor_module
|
||||
} else {
|
||||
stepDetail = dfs(flow?.value?.modules ?? [], (m) => m).find((m) => m?.id === e?.detail)
|
||||
}
|
||||
stepDetail = stepDetail ?? e?.detail
|
||||
stepDetail = stepDetail ?? nodeId
|
||||
dispatch('select', stepDetail)
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -76,9 +76,8 @@
|
||||
size="xs"
|
||||
startIcon={{ icon: Clipboard }}
|
||||
btnClasses="absolute top-2 right-2 w-min z-20"
|
||||
>
|
||||
Copy content
|
||||
</Button>
|
||||
iconOnly
|
||||
/>
|
||||
|
||||
<div class={shouldDisplayLoadMore ? 'code-container' : ''}>
|
||||
<Highlight
|
||||
|
||||
@@ -372,6 +372,10 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="p-2 border rounded">
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
||||
<input type="text" placeholder="Custom Name" bind:value={oauths[k]['display_name']} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Client Id</span>
|
||||
<input
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
>
|
||||
<input type="text" placeholder="Base URL" bind:value={baseUrl} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
||||
<input type="text" placeholder="Custom Name" bind:value={value['display_name']} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Client Id</span>
|
||||
<input type="text" placeholder="Client Id" bind:value={value['id']} />
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
>
|
||||
<input type="text" placeholder="yourorg" bind:value={org} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
||||
<input type="text" placeholder="Custom Name" bind:value={value['display_name']} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Client Id</span>
|
||||
<input type="text" placeholder="Client Id" bind:value={value['id']} />
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
export let label: string | undefined = undefined
|
||||
export let primary = false
|
||||
</script>
|
||||
|
||||
<div class={$$props.class}>
|
||||
<div class="flex flex-row justify-between items-center w-full">
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<span class="text-secondary text-sm leading-6">{label}</span>
|
||||
<span class="{primary ? 'text-primary' : 'text-secondary'} text-sm leading-6">{label}</span>
|
||||
<slot name="header" />
|
||||
</div>
|
||||
<slot name="error" />
|
||||
|
||||
@@ -55,9 +55,14 @@
|
||||
const providersType = providers.map((p) => p.type as string)
|
||||
|
||||
let showPassword = false
|
||||
let logins: string[] | undefined = undefined
|
||||
let logins: OAuthLogin[] | undefined = undefined
|
||||
let saml: string | undefined = undefined
|
||||
|
||||
type OAuthLogin = {
|
||||
type: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
async function login(): Promise<void> {
|
||||
if (!email || !password) {
|
||||
sendUserToast('Please fill in both email and password', true)
|
||||
@@ -144,7 +149,10 @@
|
||||
|
||||
async function loadLogins() {
|
||||
const allLogins = await OauthService.listOauthLogins()
|
||||
logins = allLogins.oauth
|
||||
logins = allLogins.oauth.map(login => ({
|
||||
type: login.type,
|
||||
displayName: login.display_name || login.type
|
||||
}))
|
||||
saml = allLogins.saml
|
||||
|
||||
showPassword = (logins.length == 0 && !saml) || (email != undefined && email.length > 0)
|
||||
@@ -205,26 +213,26 @@
|
||||
<Skeleton layout={[0.5, [2.375]]} />
|
||||
{/each}
|
||||
{:else}
|
||||
{#each providers as { type, icon, name }}
|
||||
{#if logins?.includes(type)}
|
||||
{#each providers as { type, icon }}
|
||||
{#if logins?.some(login => login.type === type)}
|
||||
<Button
|
||||
color="light"
|
||||
variant="border"
|
||||
startIcon={{ icon, classes: 'h-4' }}
|
||||
on:click={() => storeRedirect(type)}
|
||||
>
|
||||
{name}
|
||||
{logins.find(login => login.type === type)?.displayName}
|
||||
</Button>
|
||||
{/if}
|
||||
{/each}
|
||||
{#each logins.filter((x) => !providersType?.includes(x)) as login}
|
||||
{#each logins.filter((login) => !providersType?.includes(login.type)) as login}
|
||||
<Button
|
||||
color="dark"
|
||||
variant="border"
|
||||
btnClasses="mt-2 w-full !border-gray-300"
|
||||
on:click={() => storeRedirect(login)}
|
||||
on:click={() => storeRedirect(login.type)}
|
||||
>
|
||||
{login}
|
||||
{login.displayName}
|
||||
</Button>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
>
|
||||
{#if enabled}
|
||||
<div class="p-2 rounded border">
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
||||
<input type="text" placeholder="Custom Name" bind:value={value['display_name']} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Client Id</span>
|
||||
<input type="text" placeholder="Client Id" bind:value={value['id']} />
|
||||
|
||||
@@ -66,6 +66,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
||||
<input type="text" placeholder="Custom Name" bind:value={value['display_name']} />
|
||||
</label>
|
||||
<label class="block pb-2">
|
||||
<span class="text-primary font-semibold text-sm"
|
||||
>Client Id <Tooltip
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import FolderEditor from './FolderEditor.svelte'
|
||||
import { random_adj } from './random_positive_adjetive'
|
||||
import Required from './Required.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { Eye, Folder, Plus, SearchCode, User } from 'lucide-svelte'
|
||||
|
||||
type PathKind =
|
||||
@@ -162,7 +160,7 @@
|
||||
.map((x) => ({
|
||||
name: x,
|
||||
write:
|
||||
($userStore?.folders?.includes(x) == true ?? false) ||
|
||||
$userStore?.folders?.includes(x) == true ||
|
||||
($userStore?.is_admin ?? false) ||
|
||||
($userStore?.is_super_admin ?? false)
|
||||
}))
|
||||
@@ -322,54 +320,54 @@
|
||||
<div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 pb-0 mb-1">
|
||||
{#if meta != undefined}
|
||||
<div class="flex gap-x-4 shrink">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
{#if !hideUser}
|
||||
<div class="block">
|
||||
<span class="text-secondary text-sm whitespace-nowrap"> </span>
|
||||
|
||||
<ToggleButtonGroup
|
||||
class="mt-0.5"
|
||||
bind:selected={meta.ownerKind}
|
||||
on:selected={(e) => {
|
||||
setDirty()
|
||||
const kind = e.detail
|
||||
if (meta) {
|
||||
if (kind === 'folder') {
|
||||
meta.owner = folders?.[0]?.name ?? ''
|
||||
} else if (kind === 'group') {
|
||||
meta.owner = 'all'
|
||||
} else {
|
||||
meta.owner = $userStore?.username?.split('@')[0] ?? ''
|
||||
}
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
{#if !hideUser}
|
||||
<div class="block">
|
||||
<ToggleButtonGroup
|
||||
class="mt-0.5"
|
||||
bind:selected={meta.ownerKind}
|
||||
on:selected={(e) => {
|
||||
setDirty()
|
||||
const kind = e.detail
|
||||
if (meta) {
|
||||
if (kind === 'folder') {
|
||||
meta.owner = folders?.[0]?.name ?? ''
|
||||
} else if (kind === 'group') {
|
||||
meta.owner = 'all'
|
||||
} else {
|
||||
meta.owner = $userStore?.username?.split('@')[0] ?? ''
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleButton
|
||||
icon={User}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="user"
|
||||
position="left"
|
||||
label="User"
|
||||
/>
|
||||
<!-- <ToggleButton light size="xs" value="group" position="center">Group</ToggleButton> -->
|
||||
<ToggleButton
|
||||
icon={Folder}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="folder"
|
||||
position="right"
|
||||
label="Folder"
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleButton
|
||||
icon={User}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="user"
|
||||
position="left"
|
||||
label="User"
|
||||
/>
|
||||
<!-- <ToggleButton light size="xs" value="group" position="center">Group</ToggleButton> -->
|
||||
<ToggleButton
|
||||
icon={Folder}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="folder"
|
||||
position="right"
|
||||
label="Folder"
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !hideUser}
|
||||
<div class="text-xl">/</div>
|
||||
{/if}
|
||||
<div>
|
||||
{#if meta.ownerKind === 'user'}
|
||||
<label class="block shrink min-w-0">
|
||||
<span class="text-secondary text-sm">User</span>
|
||||
<input
|
||||
class="!w-36"
|
||||
type="text"
|
||||
@@ -381,16 +379,6 @@
|
||||
</label>
|
||||
{:else if meta.ownerKind === 'folder'}
|
||||
<label class="block grow w-48">
|
||||
<span class="text-secondary text-sm">
|
||||
Folder
|
||||
<Tooltip
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/groups_and_folders"
|
||||
>
|
||||
Read and write permissions are given to groups and users at the folder level and
|
||||
shared by all items inside the folder.
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
<div class="flex flex-row items-center gap-1 w-full">
|
||||
<select class="grow w-full" {disabled} bind:value={meta.owner}>
|
||||
{#if folders?.length == 0}
|
||||
@@ -426,14 +414,8 @@
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
<span class="text-xl">/</span>
|
||||
<label class="block grow w-full max-w-md">
|
||||
<div class="text-secondary text-sm flex items-center gap-1 w-full justify-between">
|
||||
<div>
|
||||
Name
|
||||
<Required required={true} />
|
||||
</div>
|
||||
<div class="text-2xs text-tertiary"> '/' for subfolders </div>
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
{disabled}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<script lang="ts">
|
||||
import { Badge, Button } from './common'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { base } from '$lib/base'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { ListOrdered, PenBox } from 'lucide-svelte'
|
||||
import JobArgs from './JobArgs.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import ScheduleEditor from './ScheduleEditor.svelte'
|
||||
import { type Schedule } from '$lib/gen'
|
||||
|
||||
export let schedule: any
|
||||
export let can_write: boolean
|
||||
export let path: string
|
||||
export let isFlow: boolean
|
||||
export let scheduleEditor: ScheduleEditor
|
||||
export let setScheduleEnabled: (path: string, enabled: boolean) => void
|
||||
|
||||
$: schedule = typeof schedule === 'boolean' ? undefined : (schedule as Schedule | undefined)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2 grow w-full">
|
||||
<div class="grid grid-cols-3 w-full">
|
||||
<div class="flex justify-start">
|
||||
<Badge color="indigo" small>
|
||||
Primary
|
||||
<Tooltip light>
|
||||
Share the same path as the script or flow it is attached to and its path get renamed
|
||||
whenever the source path is renamed
|
||||
</Tooltip>
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
size="9"
|
||||
class="!text-xs !h-6 !text-primary"
|
||||
type="text"
|
||||
id="cron-schedule"
|
||||
name="cron-schedule"
|
||||
placeholder="*/30 * * * *"
|
||||
value={schedule?.schedule ?? ''}
|
||||
disabled={true}
|
||||
/>
|
||||
<Toggle
|
||||
checked={schedule?.enabled ?? false}
|
||||
on:change={(e) => {
|
||||
if (can_write) {
|
||||
setScheduleEnabled(path, e.detail)
|
||||
} else {
|
||||
sendUserToast('not enough permission', true)
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'On'
|
||||
}}
|
||||
size="xs"
|
||||
textClass="text-primary font-normal text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<Button size={'xs'} variant="border" color="light" href={`${base}/runs/${path}`}>
|
||||
<span>Runs</span>
|
||||
<ListOrdered size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
on:click={() => scheduleEditor?.openEdit(path ?? '', isFlow)}
|
||||
>
|
||||
<PenBox size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if Object.keys(schedule?.args ?? {}).length > 0}
|
||||
<div class="">
|
||||
<JobArgs args={schedule?.args ?? {}} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-xs text-tertiary"> No arguments </div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -215,7 +215,7 @@
|
||||
<div>
|
||||
<form on:submit|preventDefault>
|
||||
<table class="w-full border-collapse mb-2 text-xs table-auto">
|
||||
<thead class="bg-gray-200 text-left text-xs">
|
||||
<thead class="bg-gray-200 dark:bg-slate-600 text-left text-xs">
|
||||
<tr>
|
||||
<th class="p-2 min-w-[120px]">
|
||||
Name
|
||||
@@ -251,8 +251,9 @@
|
||||
<tbody>
|
||||
{#each alerts as alert, index}
|
||||
<tr
|
||||
class:staged-for-deletion={removedAlerts.includes(alert)}
|
||||
class:non-interactable={removedAlerts.includes(alert)}
|
||||
class={removedAlerts.includes(alert)
|
||||
? 'bg-red-100 dark:bg-red-900 pointer-events-none opacity-50'
|
||||
: ''}
|
||||
>
|
||||
<td class="border p-2">
|
||||
{#if editingIndex === index}
|
||||
@@ -265,12 +266,14 @@
|
||||
{#if editingIndex === index}
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
{#each alert.tags_to_monitor as tag}
|
||||
<span class="inline-block bg-blue-100 rounded px-2 py-1 text-xs">
|
||||
<span
|
||||
class="inline-block bg-blue-100 dark:bg-blue-700 rounded px-2 py-1 text-xs"
|
||||
>
|
||||
{tag}
|
||||
<button
|
||||
on:click={() => removeTag(index, tag)}
|
||||
aria-label="Remove Tag"
|
||||
class="ml-1 text-black text-xs">x</button
|
||||
class="ml-1 text-xs">x</button
|
||||
>
|
||||
</span>
|
||||
{/each}
|
||||
@@ -290,17 +293,23 @@
|
||||
<!-- Add the new "Add All Tags" button here -->
|
||||
<button
|
||||
on:click={() => addAllTags(index)}
|
||||
class="text-xs hover:bg-gray-200 rounded px-2 py-1 mt-1"
|
||||
class="text-xs hover:bg-gray-200 dark:hover:bg-gray-700 rounded px-2 py-1 mt-1"
|
||||
>
|
||||
Add All Tags
|
||||
</button>
|
||||
{#if filteredTags.length > 0}
|
||||
<ul class="autocomplete-list">
|
||||
<ul
|
||||
class="autocomplete-list border max-h-36 overflow-y-auto absolute z-50"
|
||||
>
|
||||
{#each filteredTags as tag}
|
||||
<li>
|
||||
<button type="button" on:click={() => addTag(index, tag)}
|
||||
>{tag}</button
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left p-2 cursor-pointer hover:bg-slate-200 dark:hover:bg-slate-700"
|
||||
on:click={() => addTag(index, tag)}
|
||||
>
|
||||
{tag}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
@@ -308,7 +317,8 @@
|
||||
{:else}
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each alert.tags_to_monitor as tag}
|
||||
<span class="inline-block bg-blue-100 rounded px-2 py-1 text-xs"
|
||||
<span
|
||||
class="inline-block bg-blue-100 dark:bg-blue-700 rounded px-2 py-1 text-xs"
|
||||
>{tag}</span
|
||||
>
|
||||
{/each}
|
||||
@@ -380,9 +390,14 @@
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-gray-600 text-sm">
|
||||
Queue Metric Alerts are an enterprise feature allowing you to monitor queues for waiting jobs. Please upgrade to access this functionality.
|
||||
<a href="https://www.windmill.dev/docs/misc/plans_details" target="_blank" class="text-blue-500 underline">Learn more about our plans.</a>
|
||||
<p class="text-sm">
|
||||
Queue Metric Alerts are an enterprise feature allowing you to monitor queues for waiting
|
||||
jobs. Please upgrade to access this functionality.
|
||||
<a
|
||||
href="https://www.windmill.dev/docs/misc/plans_details"
|
||||
target="_blank"
|
||||
class="text-blue-500 underline">Learn more about our plans.</a
|
||||
>
|
||||
</p>
|
||||
{/if}
|
||||
</Section>
|
||||
@@ -391,28 +406,3 @@
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<style>
|
||||
.staged-for-deletion {
|
||||
background-color: #f8d7da; /* Light red background for staged deletion */
|
||||
}
|
||||
.non-interactable {
|
||||
pointer-events: none; /* Disable pointer events */
|
||||
opacity: 0.5; /* Reduce opacity */
|
||||
}
|
||||
.autocomplete-list {
|
||||
border: 1px solid #ccc;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
}
|
||||
.autocomplete-list li {
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.autocomplete-list li:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -249,6 +249,7 @@
|
||||
if (canSelect) {
|
||||
const ids = u.map((j) => data.datasets[j.datasetIndex].data[j.index].id)
|
||||
selectedIds = ids
|
||||
dispatch('pointClicked', ids)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,178 +1,304 @@
|
||||
<script lang="ts">
|
||||
import ScheduleEditor from './ScheduleEditor.svelte'
|
||||
import { Badge, Button } from './common'
|
||||
import { Button } from './common'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { ScheduleService, type Schedule } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { base } from '$lib/base'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { ListOrdered, Calendar, PenBox } from 'lucide-svelte'
|
||||
import JobArgs from './JobArgs.svelte'
|
||||
import { Calendar, Trash, Save } from 'lucide-svelte'
|
||||
import Skeleton from './common/skeleton/Skeleton.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { ScheduleTrigger, TriggerContext } from './triggers'
|
||||
import CronInput from './CronInput.svelte'
|
||||
import SchemaForm from './SchemaForm.svelte'
|
||||
import { emptyString, sendUserToast } from '$lib/utils'
|
||||
import Toggle from './Toggle.svelte'
|
||||
|
||||
export let schema: any
|
||||
export let isFlow: boolean
|
||||
export let path: string
|
||||
export let can_write: boolean
|
||||
export let newItem: boolean = false
|
||||
|
||||
const { primarySchedule, triggersCount } = getContext<TriggerContext>('TriggerContext')
|
||||
|
||||
let scheduleEditor: ScheduleEditor
|
||||
|
||||
let schedule: Schedule | false | undefined = undefined
|
||||
$: loadSchedules(false) || path
|
||||
|
||||
let schedules: Schedule[] | undefined = undefined
|
||||
|
||||
$: path && loadSchedule()
|
||||
$: path && loadSchedules()
|
||||
|
||||
async function loadSchedule() {
|
||||
try {
|
||||
let exists = await ScheduleService.existsSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path
|
||||
})
|
||||
if (exists) {
|
||||
schedule = await ScheduleService.getSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path
|
||||
})
|
||||
} else {
|
||||
schedule = false
|
||||
let initialPrimarySchedule: ScheduleTrigger | false | undefined = undefined
|
||||
async function loadSchedules(forceRefresh: boolean) {
|
||||
if (!path || path == '') {
|
||||
schedules = []
|
||||
if ($primarySchedule == undefined) {
|
||||
$primarySchedule = false
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('no primary schedule')
|
||||
initialPrimarySchedule = structuredClone($primarySchedule)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSchedules() {
|
||||
try {
|
||||
schedules = (
|
||||
await ScheduleService.listSchedules({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path,
|
||||
isFlow
|
||||
})
|
||||
).filter((s) => s.path != path)
|
||||
} catch (e) {
|
||||
console.error('impossible to load schedules')
|
||||
}
|
||||
}
|
||||
|
||||
async function setScheduleEnabled(path: string, enabled: boolean): Promise<void> {
|
||||
try {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { enabled }
|
||||
const allSchedules = await ScheduleService.listSchedules({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: path,
|
||||
isFlow
|
||||
})
|
||||
loadSchedule()
|
||||
const primary = allSchedules.find((s) => s.path == path)
|
||||
let remotePrimarySchedule = primary
|
||||
? {
|
||||
summary: primary.summary,
|
||||
args: primary.args ?? {},
|
||||
cron: primary.schedule,
|
||||
timezone: primary.timezone,
|
||||
enabled: primary.enabled
|
||||
}
|
||||
: false
|
||||
if ($primarySchedule == undefined || forceRefresh) {
|
||||
$primarySchedule = remotePrimarySchedule
|
||||
}
|
||||
initialPrimarySchedule = structuredClone(remotePrimarySchedule)
|
||||
|
||||
sendUserToast(`Schedule ${enabled ? 'enabled' : 'disabled'}`)
|
||||
} catch (err) {
|
||||
sendUserToast(`Cannot ` + (enabled ? 'disable' : 'enable') + ` schedule: ${err}`, true)
|
||||
loadSchedule()
|
||||
$triggersCount = {
|
||||
...($triggersCount ?? {}),
|
||||
schedule_count: allSchedules.length,
|
||||
primary_schedule: $primarySchedule ? { schedule: $primarySchedule.cron } : undefined
|
||||
}
|
||||
schedules = allSchedules.filter((s) => s.path != path)
|
||||
} catch (e) {
|
||||
console.error('impossible to load schedules', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function save() {
|
||||
const scheduleExists =
|
||||
path != '' &&
|
||||
!newItem &&
|
||||
(await ScheduleService.existsSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
path
|
||||
}))
|
||||
if (scheduleExists) {
|
||||
console.log('primary schedule exists')
|
||||
if ($primarySchedule) {
|
||||
await ScheduleService.updateSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
path,
|
||||
requestBody: {
|
||||
summary: $primarySchedule.summary,
|
||||
args: $primarySchedule.args,
|
||||
schedule: $primarySchedule.cron,
|
||||
timezone: $primarySchedule.timezone
|
||||
}
|
||||
})
|
||||
sendUserToast(`Primary schedule updated`)
|
||||
} else {
|
||||
await ScheduleService.deleteSchedule({ workspace: $workspaceStore!, path })
|
||||
sendUserToast(`Primary schedule deleted`)
|
||||
}
|
||||
} else {
|
||||
if ($primarySchedule) {
|
||||
await ScheduleService.createSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path,
|
||||
script_path: path,
|
||||
is_flow: isFlow,
|
||||
summary: $primarySchedule.summary,
|
||||
args: $primarySchedule.args,
|
||||
schedule: $primarySchedule.cron,
|
||||
timezone: $primarySchedule.timezone,
|
||||
enabled: $primarySchedule.enabled
|
||||
}
|
||||
})
|
||||
sendUserToast(`Primary schedule created`)
|
||||
}
|
||||
}
|
||||
loadSchedules(true)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ScheduleEditor
|
||||
on:update={() => {
|
||||
loadSchedule()
|
||||
loadSchedules()
|
||||
loadSchedules(true)
|
||||
}}
|
||||
bind:this={scheduleEditor}
|
||||
/>
|
||||
|
||||
<div class="p-2 flex flex-col">
|
||||
<Button
|
||||
on:click={() => scheduleEditor?.openNew(isFlow, path)}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: Calendar }}
|
||||
>
|
||||
New Schedule
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
{#if $primarySchedule == undefined}
|
||||
<Skeleton layout={[[12]]} />
|
||||
{:else if $primarySchedule}
|
||||
<div class="w-full flex flex-col mb-4">
|
||||
{#if can_write}
|
||||
<div class="w-full flex-row-reverse flex mb-2">
|
||||
<div class="flex flex-row gap-4">
|
||||
<Button
|
||||
on:click={() => {
|
||||
$primarySchedule = false
|
||||
$triggersCount = {
|
||||
...($triggersCount ?? {}),
|
||||
schedule_count: ($triggersCount?.schedule_count ?? 1) - 1,
|
||||
primary_schedule: undefined
|
||||
}
|
||||
}}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: Trash }}
|
||||
/>
|
||||
{#if initialPrimarySchedule && !newItem}
|
||||
<Toggle
|
||||
disabled={emptyString($primarySchedule.cron)}
|
||||
bind:checked={$primarySchedule.enabled}
|
||||
options={{
|
||||
right: 'Enabled'
|
||||
}}
|
||||
on:change={async (e) => {
|
||||
if (!newItem) {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path: path,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { enabled: e.detail }
|
||||
})
|
||||
|
||||
{#if schedule}
|
||||
<div class="p-2 flex flex-col gap-2">
|
||||
<div class="flex flex-row justify-between h-8">
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
class="inline-block !w-32"
|
||||
type="text"
|
||||
id="cron-schedule"
|
||||
name="cron-schedule"
|
||||
placeholder="*/30 * * * *"
|
||||
value={schedule.schedule}
|
||||
disabled={true}
|
||||
/>
|
||||
<div>
|
||||
<Badge color="indigo" small
|
||||
>Primary <Tooltip light
|
||||
>Share the same path as the script or flow it is attached to and its path get renamed
|
||||
whenever the source path is renamed</Tooltip
|
||||
></Badge
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<Toggle
|
||||
checked={schedule.enabled}
|
||||
on:change={(e) => {
|
||||
if (can_write) {
|
||||
setScheduleEnabled(path, e.detail)
|
||||
} else {
|
||||
sendUserToast('not enough permission', true)
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'On'
|
||||
}}
|
||||
size="xs"
|
||||
/>
|
||||
<Button size="xs" variant="border" color="light" href={`${base}/runs/${path}`}>
|
||||
<div class="flex flex-row gap-2">
|
||||
<ListOrdered size={14} />
|
||||
Runs
|
||||
sendUserToast(`${e.detail ? 'enabled' : 'disabled'} schedule ${path}`)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !newItem}
|
||||
<Button
|
||||
on:click={save}
|
||||
color="dark"
|
||||
size="sm"
|
||||
startIcon={{ icon: Save }}
|
||||
disabled={JSON.stringify({ ...$primarySchedule, enabled: true }) ==
|
||||
JSON.stringify({ ...initialPrimarySchedule, enabled: true })}
|
||||
>Apply changes now</Button
|
||||
>
|
||||
{:else}
|
||||
<div class="text-sm text-secondary mt-1 text-center"
|
||||
>Deployed automatically with {isFlow ? 'flow' : 'script'}</div
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
on:click={() => scheduleEditor?.openEdit(path ?? '', isFlow)}
|
||||
>
|
||||
<PenBox size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{#if Object.keys(schedule?.args ?? {}).length > 0}
|
||||
<div class="">
|
||||
<JobArgs args={schedule.args ?? {}} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-xs texg-gray-700"> No arguments </div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if schedule == undefined}
|
||||
<Skeleton layout={[[6]]} />
|
||||
{/if}
|
||||
|
||||
<h3 class="px-2 pt-4">Other schedules</h3>
|
||||
|
||||
{#if schedules}
|
||||
{#if schedules.length == 0}
|
||||
<div class="text-xs text-secondary px-2"> No other schedules </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y px-2 pt-2">
|
||||
{#each schedules as schedule (schedule.path)}
|
||||
<div class="grid grid-cols-6 text-2xs items-center py-2"
|
||||
><div class="col-span-3 truncate">{schedule.path}</div><div>{schedule.schedule}</div>
|
||||
<div>{schedule.enabled ? 'on' : 'off'}</div>
|
||||
<button on:click={() => scheduleEditor?.openEdit(schedule.path, isFlow)}>Edit</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<div class="mt-5">
|
||||
<Label label="Summary" class="font-semibold" primary>
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
class="text-sm w-full"
|
||||
bind:value={$primarySchedule.summary}
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
<CronInput bind:schedule={$primarySchedule.cron} bind:timezone={$primarySchedule.timezone} />
|
||||
<SchemaForm {schema} bind:args={$primarySchedule.args} />
|
||||
{#if emptyString($primarySchedule.cron)}
|
||||
<p class="text-xs text-tertiary mt-10">Define a schedule frequency first</p>
|
||||
{/if}
|
||||
|
||||
{#if initialPrimarySchedule != false}
|
||||
<div class="flex">
|
||||
<Button size="xs" color="light" on:click={() => scheduleEditor?.openEdit(path, isFlow)}
|
||||
>Advanced</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="flex flex-row gap-4 mt-2">
|
||||
<Button
|
||||
on:click={() => {
|
||||
$primarySchedule = {
|
||||
summary: '',
|
||||
args: {},
|
||||
cron: '0 0 0 /1 * * *',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
enabled: true
|
||||
}
|
||||
$triggersCount = {
|
||||
...($triggersCount ?? {}),
|
||||
schedule_count: ($triggersCount?.schedule_count ?? 0) + 1,
|
||||
primary_schedule: { schedule: $primarySchedule.cron }
|
||||
}
|
||||
}}
|
||||
variant="contained"
|
||||
color="dark"
|
||||
size="md"
|
||||
startIcon={{ icon: Calendar }}
|
||||
>
|
||||
Set Primary Schedule
|
||||
</Button>
|
||||
{#if initialPrimarySchedule != undefined && initialPrimarySchedule != false && !newItem}
|
||||
<Button on:click={save} color="dark" size="md" startIcon={{ icon: Save }}>
|
||||
Apply changes now
|
||||
</Button>
|
||||
{:else}
|
||||
<div class="text-sm text-center text-secondary mt-2"
|
||||
>Deployed automatically with {isFlow ? 'flow' : 'script'}</div
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Label label="Summary" class="font-semibold" primary>
|
||||
<input
|
||||
type="text"
|
||||
disabled
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
class="text-sm w-full"
|
||||
/>
|
||||
</Label>
|
||||
<CronInput schedule={''} disabled timezone={Intl.DateTimeFormat().resolvedOptions().timeZone} />
|
||||
|
||||
<SchemaForm disabled {schema} />
|
||||
{/if}
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
|
||||
{#if !newItem}
|
||||
<div class="mt-10" />
|
||||
{#if $primarySchedule}
|
||||
<Button
|
||||
on:click={() => scheduleEditor?.openNew(isFlow, path)}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: Calendar }}
|
||||
>
|
||||
New Schedule
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
<Label label="Other schedules">
|
||||
{#if schedules}
|
||||
{#if schedules?.length == 0 || schedules == undefined}
|
||||
<div class="text-xs text-tertiary"> No other schedules </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y">
|
||||
{#each schedules as schedule (schedule.path)}
|
||||
<div class="grid grid-cols-6 text-xs items-center py-2">
|
||||
<div class="col-span-3 truncate">{schedule.path}</div>
|
||||
<div class="col-span-2 flex flex-row gap-4 flex-nowrap">
|
||||
<div>{schedule.schedule}</div>
|
||||
<div>{schedule.enabled ? 'on' : 'off'}</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => scheduleEditor?.openEdit(schedule.path, isFlow)}
|
||||
class="px-2">Edit</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
</Label>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -520,13 +520,14 @@
|
||||
<Toggle
|
||||
disabled={!can_write}
|
||||
checked={enabled}
|
||||
options={{ right: 'enable', left: 'disable' }}
|
||||
options={{ right: 'Enabled' }}
|
||||
on:change={async (e) => {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { enabled: e.detail }
|
||||
})
|
||||
dispatch('update')
|
||||
sendUserToast(`${e.detail ? 'enabled' : 'disabled'} schedule ${initialPath}`)
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
schema.properties = n
|
||||
}
|
||||
let nkeys = Object.keys(schema.properties ?? {})
|
||||
|
||||
if (!deepEqual(keys, nkeys)) {
|
||||
keys = nkeys
|
||||
dispatch('change')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Schema } from '$lib/common'
|
||||
import { emptySchema } from '$lib/utils'
|
||||
import { copyToClipboard, emptySchema } from '$lib/utils'
|
||||
|
||||
import Highlight from 'svelte-highlight'
|
||||
import json from 'svelte-highlight/languages/json'
|
||||
@@ -10,6 +10,8 @@
|
||||
import Tabs from './common/tabs/Tabs.svelte'
|
||||
import { Badge } from './common'
|
||||
import HighlightTheme from './HighlightTheme.svelte'
|
||||
import Button from './common/button/Button.svelte'
|
||||
import { ClipboardCopy } from 'lucide-svelte'
|
||||
|
||||
export let schema: Schema | undefined | any = emptySchema()
|
||||
|
||||
@@ -79,7 +81,19 @@
|
||||
{/if}
|
||||
</TabContent>
|
||||
<TabContent value="advanced">
|
||||
<Highlight language={json} code={JSON.stringify(schema, null, 4)} />
|
||||
<div class="h-full relative">
|
||||
<Button
|
||||
wrapperClasses="absolute top-2 right-2 z-20"
|
||||
on:click={() => copyToClipboard(JSON.stringify(schema, null, 4))}
|
||||
color="light"
|
||||
size="xs2"
|
||||
startIcon={{
|
||||
icon: ClipboardCopy
|
||||
}}
|
||||
iconOnly
|
||||
/>
|
||||
<Highlight language={json} code={JSON.stringify(schema, null, 4)} />
|
||||
</div>
|
||||
</TabContent>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user