mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-29 00:03:09 +00:00
Merge remote-tracking branch 'origin/main' into win-1084-more-ansible-improvements
This commit is contained in:
@@ -10,4 +10,4 @@ WM_IMAGE=ghcr.io/windmill-labs/windmill:main
|
||||
|
||||
# To rotate logs, set the following variables:
|
||||
#LOG_MAX_SIZE=10m
|
||||
#LOG_MAX_FILE=3
|
||||
#LOG_MAX_FILE=3
|
||||
@@ -24,4 +24,4 @@ sed -i -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
|
||||
sed -i -zE "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" ${root_dirpath}/backend/Cargo.lock
|
||||
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only --ignore-scripts
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -111,8 +111,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RHEL9-amd64 build
|
||||
path:
|
||||
${{ steps.extract-ee-amd64.outputs.destination
|
||||
path: ${{ steps.extract-ee-amd64.outputs.destination
|
||||
}}/windmill-ee-amd64-rhel9
|
||||
|
||||
# - uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=embedding,parquet,openidconnect,license,http_trigger,zip,oauth2,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
features=embedding,parquet,openidconnect,license,http_trigger,zip,oauth2,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME:
|
||||
${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
IMAGE_NAME: ${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
'workflow_dispatch' && github.repository || 'windmill-labs/windmill-test' }}
|
||||
DEV_SHA:
|
||||
${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
DEV_SHA: ${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
'workflow_dispatch' && 'dev' || github.event.inputs.tag || github.sha }}
|
||||
name: Build windmill:main
|
||||
on:
|
||||
@@ -40,8 +38,7 @@ permissions: write-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubicloud
|
||||
if:
|
||||
(github.event_name != 'workflow_dispatch') || (github.event.inputs &&
|
||||
if: (github.event_name != 'workflow_dispatch') || (github.event.inputs &&
|
||||
!github.event.inputs.ee)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -95,7 +92,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages
|
||||
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages,deno_core
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
@@ -157,7 +154,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,deno_core
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
@@ -315,7 +312,7 @@ jobs:
|
||||
needs: [run_integration_test, build]
|
||||
if:
|
||||
github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' ||
|
||||
startsWith(github.ref, 'refs/tags/v')) && (github.event_name != 'workflow_dispatch')
|
||||
startsWith(github.ref, 'refs/tags/v')) && (github.event_name != 'workflow_dispatch')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -355,7 +352,7 @@ jobs:
|
||||
verify_ee_image_vulnerabilities:
|
||||
runs-on: ubicloud
|
||||
needs: [tag_latest_ee]
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (github.event_name != 'workflow_dispatch')
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (github.event_name != 'workflow_dispatch')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -397,8 +394,7 @@ jobs:
|
||||
build_ee_nsjail:
|
||||
needs: [build_ee]
|
||||
runs-on: ubicloud
|
||||
if:
|
||||
(github.event_name != 'pull_request') && ((github.event_name != 'workflow_dispatch') || (github.event.inputs.ee || github.event.inputs.nsjail))
|
||||
if: (github.event_name != 'pull_request') && ((github.event_name != 'workflow_dispatch') || (github.event.inputs.ee || github.event.inputs.nsjail))
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -437,7 +433,7 @@ jobs:
|
||||
run: |
|
||||
sed -i 's|FROM ghcr.io/windmill-labs/windmill-ee:dev|FROM ghcr.io/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}|' ./docker/DockerfileNsjail
|
||||
cat ./docker/DockerfileNsjail | grep "FROM"
|
||||
|
||||
|
||||
- name: Build and push publicly ee
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
@@ -451,12 +447,10 @@ jobs:
|
||||
${{ steps.meta-ee-public.outputs.labels }}
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
|
||||
publish_ecr_s3:
|
||||
needs: [build_ee_nsjail]
|
||||
runs-on: ubicloud-standard-2-arm
|
||||
if:
|
||||
(github.event_name != 'pull_request') && (github.event_name !=
|
||||
if: (github.event_name != 'pull_request') && (github.event_name !=
|
||||
'workflow_dispatch')
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
|
||||
@@ -1,5 +1,93 @@
|
||||
# Changelog
|
||||
|
||||
## [1.483.2](https://github.com/windmill-labs/windmill/compare/v1.483.1...v1.483.2) (2025-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* batch reruns query missing workspace_id check in subquery ([#5652](https://github.com/windmill-labs/windmill/issues/5652)) ([444a6ab](https://github.com/windmill-labs/windmill/commit/444a6abad670114c52e44f3606bf6fefc5d3fd98))
|
||||
* **frontend:** fix validity check ([#5654](https://github.com/windmill-labs/windmill/issues/5654)) ([c41c1eb](https://github.com/windmill-labs/windmill/commit/c41c1eb587bf22364f1202310a0c64b6040ab968))
|
||||
* improve MySQL datetime parser timezone handling (WIN-1155) ([#5645](https://github.com/windmill-labs/windmill/issues/5645)) ([5bca8f6](https://github.com/windmill-labs/windmill/commit/5bca8f60e970cc67839edb5dc491685f36cf0499))
|
||||
* track relative imports in python and ts even if lockfile is provided ([e316dbd](https://github.com/windmill-labs/windmill/commit/e316dbd9bdd5c59e9aaba6a4472bb7d832834e84))
|
||||
|
||||
## [1.483.1](https://github.com/windmill-labs/windmill/compare/v1.483.0...v1.483.1) (2025-04-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* pin libxml to 0.3.3 ([e5595e4](https://github.com/windmill-labs/windmill/commit/e5595e41b5c87704d814eff95bc00b82195728ba))
|
||||
|
||||
## [1.483.0](https://github.com/windmill-labs/windmill/compare/v1.482.1...v1.483.0) (2025-04-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* handle different aws auth resource type ([#5637](https://github.com/windmill-labs/windmill/issues/5637)) ([5b123b0](https://github.com/windmill-labs/windmill/commit/5b123b01a1318208450789b5bcade447a0b331c7))
|
||||
* oidc support for sqs trigger ([#5614](https://github.com/windmill-labs/windmill/issues/5614)) ([34b307b](https://github.com/windmill-labs/windmill/commit/34b307b2be1f6cf92a81694325f4c333bdd7b055))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix click outside popover fullscreen ([#5631](https://github.com/windmill-labs/windmill/issues/5631)) ([0811457](https://github.com/windmill-labs/windmill/commit/081145726a5d4ab81510a7637126a036823a1565))
|
||||
* improve flow editor step switch performance ([58fa4c8](https://github.com/windmill-labs/windmill/commit/58fa4c80062a5704bbd13ddda1b2f00c7c9e40dd))
|
||||
* linter in early stop doesn't include flow_input ([#5638](https://github.com/windmill-labs/windmill/issues/5638)) ([6a9bdfd](https://github.com/windmill-labs/windmill/commit/6a9bdfd3bd52ff802b6a71c3ae9504bfe7d0421f))
|
||||
* output picker output opening doesn't change id ([#5641](https://github.com/windmill-labs/windmill/issues/5641)) ([64c72b6](https://github.com/windmill-labs/windmill/commit/64c72b6fce669e47f04dc620750840857cbe66cf))
|
||||
|
||||
## [1.482.1](https://github.com/windmill-labs/windmill/compare/v1.482.0...v1.482.1) (2025-04-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* flow editor workspace script test use actual workspace script hash ([24e893b](https://github.com/windmill-labs/windmill/commit/24e893b8c50fafdb41f4b6e1777cb34aceafc466))
|
||||
* **frontend:** postgres remove selectedTable ([#5386](https://github.com/windmill-labs/windmill/issues/5386)) ([bd7c6a2](https://github.com/windmill-labs/windmill/commit/bd7c6a2a46047de5fe89753decdfdf1f4851ee3f))
|
||||
* **openapi:** fix openapi def of batch re-run jobs ([cb8731e](https://github.com/windmill-labs/windmill/commit/cb8731e7e37fb6cd052f5dae6fdce46e6ca2409c))
|
||||
* show workspace color if superadmin and not in workspace + change workspace name when switching workspace ([#5625](https://github.com/windmill-labs/windmill/issues/5625)) ([cc4384f](https://github.com/windmill-labs/windmill/commit/cc4384f48cc89f883237a2082d854d69a7b5dc56))
|
||||
|
||||
## [1.482.0](https://github.com/windmill-labs/windmill/compare/v1.481.0...v1.482.0) (2025-04-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add diff toggle to flow inline scripts ([#5550](https://github.com/windmill-labs/windmill/issues/5550)) ([b3ecde3](https://github.com/windmill-labs/windmill/commit/b3ecde3316252bcd7323de98149786349019ba7e))
|
||||
* add gcp trigger ([#5501](https://github.com/windmill-labs/windmill/issues/5501)) ([6339775](https://github.com/windmill-labs/windmill/commit/63397754046eed41d32e28d4698db37b4c9b9710))
|
||||
* add wildcards filter for worker/label/tags ([62f14d1](https://github.com/windmill-labs/windmill/commit/62f14d1cb95e3f1c7de85c46e1c6bb092247656c))
|
||||
* add windmill context to autocomplete ([#5548](https://github.com/windmill-labs/windmill/issues/5548)) ([b47c151](https://github.com/windmill-labs/windmill/commit/b47c15165f93ca68a58f81cf2b86fc9467155482))
|
||||
* agent workers v2 using http ([#5588](https://github.com/windmill-labs/windmill/issues/5588)) ([63fa499](https://github.com/windmill-labs/windmill/commit/63fa4990153f33434b49269922f7803d04e407cd))
|
||||
* Batch re-run ([#5553](https://github.com/windmill-labs/windmill/issues/5553)) ([26b5ea5](https://github.com/windmill-labs/windmill/commit/26b5ea5023a100c57d077910c99a5e5703edf1c1))
|
||||
* **frontend:** app editor code input component (monaco) ([#5566](https://github.com/windmill-labs/windmill/issues/5566)) ([177e16b](https://github.com/windmill-labs/windmill/commit/177e16bb18eed0d1c454b967aaa59547f61e8d26))
|
||||
* handle sending selected lines to ai context ([#5527](https://github.com/windmill-labs/windmill/issues/5527)) ([5abdc3e](https://github.com/windmill-labs/windmill/commit/5abdc3e4403b5c604309bd99a24d7a2847a17b9b))
|
||||
* Implement sending diff to ai ([#5510](https://github.com/windmill-labs/windmill/issues/5510)) ([e118d2c](https://github.com/windmill-labs/windmill/commit/e118d2cd5f9c641884a76229802a5228ef41f1a5))
|
||||
* make azure a standalone AI provider ([#5558](https://github.com/windmill-labs/windmill/issues/5558)) ([2c5e58c](https://github.com/windmill-labs/windmill/commit/2c5e58cf1ab9225d516540b38d9e4dde482a3a7f))
|
||||
* migrate to svelte5 + vite6 ([#4813](https://github.com/windmill-labs/windmill/issues/4813)) ([3c99b3f](https://github.com/windmill-labs/windmill/commit/3c99b3fdc7b78b1cdc7d8fb21d999296695f7889))
|
||||
* **postgres-trigger:** postgres trigger fix circular dependencies and add remove associate resource ([#5606](https://github.com/windmill-labs/windmill/issues/5606)) ([1daeb2f](https://github.com/windmill-labs/windmill/commit/1daeb2f48f3026621b3ffc58e10f048d5911906c))
|
||||
* **python:** per import requirement pin ([#5520](https://github.com/windmill-labs/windmill/issues/5520)) ([0b6d017](https://github.com/windmill-labs/windmill/commit/0b6d017fedc31e790a76cf29a1adaaf2a72acc61))
|
||||
* signed s3 objects ([#5593](https://github.com/windmill-labs/windmill/issues/5593)) ([b9e8796](https://github.com/windmill-labs/windmill/commit/b9e879618bc223ce17effde8bb4c5d1df2ad6df5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add support for ${} syntax without default in bash ([#5594](https://github.com/windmill-labs/windmill/issues/5594)) ([3950cfd](https://github.com/windmill-labs/windmill/commit/3950cfd7e3297d7f8ec56430d6462f6b67ecd3c2))
|
||||
* app editor svelte 5 fixes ([#5570](https://github.com/windmill-labs/windmill/issues/5570)) ([b926076](https://github.com/windmill-labs/windmill/commit/b9260769883348ecd5aeb5684f527a8bf0073928))
|
||||
* binding not working in nested array script arg ([#5585](https://github.com/windmill-labs/windmill/issues/5585)) ([f5d46d5](https://github.com/windmill-labs/windmill/commit/f5d46d5751bc875b7f4da1db06be40571ac55ab8))
|
||||
* **cli:** properly handle enabled/disabled updates of schedules ([2629458](https://github.com/windmill-labs/windmill/commit/26294584d6c2ca02bbc4fc5f28cb8df6a5fb3790))
|
||||
* **cli:** wmill-locks improvement ([8d062c4](https://github.com/windmill-labs/windmill/commit/8d062c47ecd9e84a81140d5c59814da9217dd434))
|
||||
* Dynamic select does not work with tag //native ([#5576](https://github.com/windmill-labs/windmill/issues/5576)) ([1f3e7d9](https://github.com/windmill-labs/windmill/commit/1f3e7d9029051832db6ab1755b3cad38176a9e96)), closes [#5490](https://github.com/windmill-labs/windmill/issues/5490)
|
||||
* fix list jobs by tag ([0c3cb37](https://github.com/windmill-labs/windmill/commit/0c3cb3700a3fb9b69e396487bd7491dbbd8861c0))
|
||||
* flow editor svelte 5 issues ([#5567](https://github.com/windmill-labs/windmill/issues/5567)) ([4f6be6e](https://github.com/windmill-labs/windmill/commit/4f6be6ed340e26bf1ed95398a9dc9f1eb41b33dd))
|
||||
* freeze when clicking script history diff button ([#5581](https://github.com/windmill-labs/windmill/issues/5581)) ([07094b6](https://github.com/windmill-labs/windmill/commit/07094b6aa21f10688b138d2a81d4fd5833f003fc))
|
||||
* **frontend:** app builder - force json configuration in rich result ([#5565](https://github.com/windmill-labs/windmill/issues/5565)) ([6fae3a5](https://github.com/windmill-labs/windmill/commit/6fae3a566be06dae88ece8ec23f5723cd8f3f2b9))
|
||||
* **frontend:** load all step jobs ([#5617](https://github.com/windmill-labs/windmill/issues/5617)) ([16bed59](https://github.com/windmill-labs/windmill/commit/16bed593dfd0b735a92d0928df5091547b98ae79))
|
||||
* **frontend:** prevent deploy popover to show if deploy dropdown is open ([#5542](https://github.com/windmill-labs/windmill/issues/5542)) ([c2180c6](https://github.com/windmill-labs/windmill/commit/c2180c6eb34e14fe2292ff40aa6a99c627698d5e))
|
||||
* **frontend:** proper each block binding + better app settings reactivity ([#5568](https://github.com/windmill-labs/windmill/issues/5568)) ([4c71af8](https://github.com/windmill-labs/windmill/commit/4c71af8a74627d0ba76917e0dac0ac9e5e984cca))
|
||||
* improve app image picker UX ([#5589](https://github.com/windmill-labs/windmill/issues/5589)) ([f497a4b](https://github.com/windmill-labs/windmill/commit/f497a4bfae8d1bff097e0c2c9df8381a531dfeb9))
|
||||
* legacy script gen model selection ([#5574](https://github.com/windmill-labs/windmill/issues/5574)) ([3507925](https://github.com/windmill-labs/windmill/commit/3507925624a43804a3be463b6f7913cea5821384))
|
||||
* mssql ca_cert deserializing ([#5587](https://github.com/windmill-labs/windmill/issues/5587)) ([b4f8c88](https://github.com/windmill-labs/windmill/commit/b4f8c88c19bd4f844c3ecb53ececc340ee326b0e))
|
||||
* number input in app multiselect yields NOT_NUMBER ([#5616](https://github.com/windmill-labs/windmill/issues/5616)) ([4aae6ab](https://github.com/windmill-labs/windmill/commit/4aae6ab634280adc1de9abd890100b7c12c89158))
|
||||
* prevent invalid returned ai completion object errors ([#5564](https://github.com/windmill-labs/windmill/issues/5564)) ([9276c71](https://github.com/windmill-labs/windmill/commit/9276c717a21aaee3241845a9cc00d3fb6bce9eb9))
|
||||
* Remaining svelte 5 bugs ([#5563](https://github.com/windmill-labs/windmill/issues/5563)) ([6e9ec63](https://github.com/windmill-labs/windmill/commit/6e9ec6323c265a747ef8696865297e6d47abb016))
|
||||
* tenant id to never be undefined on teams ([#5572](https://github.com/windmill-labs/windmill/issues/5572)) ([102b58a](https://github.com/windmill-labs/windmill/commit/102b58a5f40dde22f15700d4b6c11eb7f3fbf4bb))
|
||||
* validate saved module before passing to flow module editor ([#5580](https://github.com/windmill-labs/windmill/issues/5580)) ([2eb1a16](https://github.com/windmill-labs/windmill/commit/2eb1a161d15627b440195b65eec54998561f4ef6))
|
||||
|
||||
## [1.481.0](https://github.com/windmill-labs/windmill/compare/v1.480.1...v1.481.0) (2025-04-02)
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ FROM node:20-alpine as frontend
|
||||
# install dependencies
|
||||
WORKDIR /frontend
|
||||
COPY ./frontend/package.json ./frontend/package-lock.json ./
|
||||
COPY ./frontend/scripts/ ./scripts/
|
||||
RUN npm ci
|
||||
|
||||
# Copy all local files into the image.
|
||||
|
||||
+28
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -45,56 +45,71 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "subscription_mode: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "gcp_subscription_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"create_update",
|
||||
"existing"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 8,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -118,6 +133,7 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
@@ -125,5 +141,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f4f6336fc671b00bed7835124892f7a4d3bbe673f7c48153819dab385a5cb357"
|
||||
"hash": "1312b7fd622cc814a406c85dbbff61f003c29185c267642cfd898075ebda855d"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT jsonb_build_object(\n 'kind', jb.kind,\n 'script_path', jb.runnable_path,\n 'latest_schema', COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow')\n ),\n 'schemas', ARRAY(\n SELECT jsonb_build_object(\n 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),\n 'job_ids', ARRAY_AGG(DISTINCT j.id),\n 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]\n ) FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'\n WHERE j.id = ANY(ARRAY_AGG(jb.id))\n GROUP BY COALESCE(s.hash, f.id)\n )\n ) FROM v2_job jb\n WHERE (jb.kind = 'flow' OR jb.kind = 'script')\n AND jb.workspace_id = $1 AND jb.id = ANY($2)\n GROUP BY jb.kind, jb.runnable_path",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "jsonb_build_object",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4"
|
||||
}
|
||||
+28
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -45,56 +45,71 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "subscription_mode: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "gcp_subscription_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"create_update",
|
||||
"existing"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 8,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -115,6 +130,7 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
@@ -122,5 +138,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "15fbe481789a7817bf37415fb935f9ed537fd7d3b266d928af4d5d3dd8bb5c18"
|
||||
"hash": "2dcff8b16da75740c362015b2293f578a21813f038d75184a5034c37d4daf36e"
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n j.id,\n j.kind AS \"kind: _\",\n COALESCE(s.path, f.path) AS \"script_path!\",\n COALESCE(s.hash, f.id) AS \"script_hash!: _\",\n COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS \"scheduled_for!: _\",\n args AS input\n FROM v2_job j\n LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script'\n LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow'\n LEFT JOIN v2_job_completed jc ON jc.id = j.id\n LEFT JOIN v2_job_queue jq ON jq.id = j.id\n WHERE j.id = ANY($1)\n AND j.workspace_id = $2\n AND COALESCE(s.hash, f.id) IS NOT NULL\n AND COALESCE(s.path, f.path) IS NOT NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "kind: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlescriptflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_path!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "script_hash!: _",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "scheduled_for!: _",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "input",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3376b42d273c2499f3517c7754f4cdb6eae2e71e56bc8e5215559af967c81bd8"
|
||||
}
|
||||
+32
-16
@@ -1,80 +1,95 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "aws_auth_resource_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "aws_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"ordinal": 2,
|
||||
"name": "message_attributes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 3,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 5,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 6,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 7,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 8,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -86,6 +101,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
@@ -103,5 +119,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2ef82fad8a6ccdc66228cfbce5393de351653ab9ac171fa0eea447c905440867"
|
||||
"hash": "5237f9d3f57f4e799968a4a9630c984775d23c7a4dc1a7ad37528a46ab89d6bd"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by)\n VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by, raw_app)\n VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -13,12 +13,13 @@
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text",
|
||||
"Varchar"
|
||||
"Varchar",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2a3992b5e9abcfbb032d10e142d98efa969dae26a7242eb7ac12593ed5421ef3"
|
||||
"hash": "56264b88a9f428e79c6e531a0011e0dbed634f3b12dedaa5fee0362d670887f6"
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT jsonb_build_object(\n 'kind', jb.kind,\n 'script_path', jb.runnable_path,\n 'latest_schema', COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')\n ),\n 'schemas', ARRAY(\n SELECT jsonb_build_object(\n 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),\n 'job_ids', ARRAY_AGG(DISTINCT j.id),\n 'schema', ANY_VALUE(COALESCE(s.schema, f.schema))\n ) FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'\n WHERE j.id = ANY(ARRAY_AGG(jb.id))\n GROUP BY COALESCE(s.hash, f.id)\n )\n ) FROM v2_job jb\n WHERE (jb.kind = 'flow' OR jb.kind = 'script')\n AND jb.workspace_id = $1 AND jb.id = ANY($2)\n GROUP BY jb.kind, jb.runnable_path",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "jsonb_build_object",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5ea1d8c87a17690942ca9e70e0b953e3b32a9a7a73784219d48f8e8932cdb0c8"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n aws_auth_resource_type,\n aws_resource_path,\n queue_url,\n message_attributes,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4, \n $5, \n $6, \n $7,\n $8,\n $9,\n $10,\n $11\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
{
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "776909d3452aaf9e0ed41a5ec314b7bfc5bc8b6a76c98bf4daf7d715d18c52e6"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n sqs_trigger \n SET \n aws_auth_resource_type = $1,\n aws_resource_path = $2,\n queue_url = $3,\n message_attributes = $4, \n is_flow = $5, \n edited_by = $6, \n email = $7,\n script_path = $8,\n path = $9,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $10 AND \n path = $11\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
{
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8490024b96aa689d3c1bc5cbf94fcd8c5491732818404fee2397f00cccee0ad3"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, path, url, script_path, is_flow, edited_by, email, edited_at, server_id, last_server_ping, extra_perms, error, enabled, filters as \"filters: _\", initial_messages as \"initial_messages: _\", url_runnable_args as \"url_runnable_args: _\", can_return_message FROM websocket_trigger\n WHERE workspace_id = $1",
|
||||
"query": "\n SELECT \n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n filters AS \"filters: _\",\n initial_messages AS \"initial_messages: _\",\n url_runnable_args AS \"url_runnable_args: _\",\n can_return_message\n FROM \n websocket_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -114,5 +114,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "1aa8ead10f8d994f6685d266fcbd409b0fff43111d9600e64b2348401ed8929d"
|
||||
"hash": "8eabeee5527da4aad3f46ad185015262b4304667449f3e1a71efd7477a39f7fc"
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n aws_resource_path,\n queue_url,\n message_attributes,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4, \n $5, \n $6, \n $7,\n $8,\n $9,\n $10\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9b9bc21023f10a0b4bb45879c7da8e119bffd2982b97fc900358506a0a14bbb8"
|
||||
}
|
||||
+48
-32
@@ -1,80 +1,95 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT * FROM sqs_trigger\n WHERE workspace_id = $1",
|
||||
"query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
"name": "aws_auth_resource_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "aws_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 2,
|
||||
"name": "message_attributes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "script_path",
|
||||
"ordinal": 3,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -85,7 +100,6 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
@@ -95,6 +109,8 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -102,5 +118,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2b6c13191484b0c664f35e2c811082b00c44fd4a7e98a11b714133674a7b6da7"
|
||||
"hash": "a7df493316f632fc636e4c3c90bef4c98035b5dc808bb8379251c0b35d945ba0"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by)\n VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by, raw_app)\n VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -13,12 +13,13 @@
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text",
|
||||
"Varchar"
|
||||
"Varchar",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "83d18f4e1cda2c1867168551d855d5626e766b5469c6976c986bdafc8b9407c5"
|
||||
"hash": "a9f6e5720f748418ae52cf0e31778b9bbef26008803f2dd2a043bdb091b2ff20"
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n j.kind AS \"kind!: JobKind\",\n j.runnable_id AS \"script_hash: _\",\n j.runnable_path AS script_path,\n COUNT(*) AS \"count!\",\n ANY_VALUE(COALESCE(f.schema, s.schema)) AS schema\n FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND f.path = j.runnable_path AND j.kind = 'flow'\n WHERE COALESCE(s.hash, f.id) IS NOT NULL\n AND COALESCE(s.path, f.path) IS NOT NULL\n AND j.workspace_id = $1 AND j.id = ANY($2)\n GROUP BY j.runnable_id, j.runnable_path, j.kind",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "kind!: JobKind",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlescriptflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "script_hash: _",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "count!",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "schema",
|
||||
"type_info": "Json"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "b61a00e6a8ca0a1d24e64fdc9223b077c957da8455ea747b0680923d8282425b"
|
||||
}
|
||||
+32
-16
@@ -1,80 +1,95 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n queue_url,\n aws_resource_path,\n message_attributes,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n sqs_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n queue_url,\n aws_resource_path,\n message_attributes,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n sqs_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "aws_auth_resource_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"ordinal": 2,
|
||||
"name": "aws_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 3,
|
||||
"name": "message_attributes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 5,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 6,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 7,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 8,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -83,6 +98,7 @@
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
@@ -100,5 +116,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e6adaebcade2e25be800e8b888b23c94caae2421f3cae7c06d6346bd6de1d94a"
|
||||
"hash": "bc0bedddcafad216c30c5061eef3f4ed8573cda89f655d6490d22f182e3f2f36"
|
||||
}
|
||||
+28
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -45,56 +45,71 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "subscription_mode: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "gcp_subscription_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"create_update",
|
||||
"existing"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 8,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -117,6 +132,7 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
@@ -124,5 +140,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "0102308ffa1c0dbfba54d29246535bb81146a4cfae0ec408435570e0813a3bef"
|
||||
"hash": "c2925a28212265bd9ac8e2d498b3588fc040c7a29ae6d9bab1f05af0b535e2ac"
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT DISTINCT ON (j.runnable_path, j.kind) \n j.kind AS \"kind!: JobKind\",\n j.runnable_path AS script_path,\n NULL as \"script_hash: _\",\n -1::bigint as \"count!: _\",\n COALESCE(f.schema, s.schema) AS schema\n FROM v2_job j\n LEFT JOIN script s ON s.path = j.runnable_path AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.path = j.runnable_path AND j.kind = 'flow'\n WHERE COALESCE(s.hash, f.id) IS NOT NULL\n AND j.workspace_id = $1 AND j.id = ANY($2)\n ORDER BY j.runnable_path, j.kind, COALESCE(f.created_at, s.created_at) DESC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "kind!: JobKind",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlescriptflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_hash: _",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "count!: _",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "schema",
|
||||
"type_info": "Json"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "c4f382045e5c47986e02f1e57667e6b3cb1d60f15fed9a24e3f05b1bd60c0fc8"
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT j.kind AS \"kind: _\", COALESCE(s.path, f.path) AS \"script_path!\", COALESCE(s.hash, f.id) AS \"script_hash!: _\", args\n FROM v2_job j\n LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script'\n LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow'\n WHERE j.id = ANY($1)\n AND j.workspace_id = $2\n AND COALESCE(s.hash, f.id) IS NOT NULL\n AND COALESCE(s.path, f.path) IS NOT NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "kind: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlescriptflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "script_path!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_hash!: _",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "args",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "ccd4c7fe5fbdf1ab4bbbbd7d2a9b1ec3fc844ed286ff563f0c9a5c4778cfb718"
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n sqs_trigger \n SET \n aws_resource_path = $1,\n queue_url = $2,\n message_attributes = $3, \n is_flow = $4, \n edited_by = $5, \n email = $6,\n script_path = $7,\n path = $8,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $9 AND \n path = $10\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f68d23841e0e31cdf8633aaf0f32777e04e4965682e5b37afbe84194b756d5f5"
|
||||
}
|
||||
Vendored
+1
-1
@@ -11,5 +11,5 @@
|
||||
"remote.autoForwardPorts": true,
|
||||
"conventionalCommits.scopes": [
|
||||
"restructring triggers, decoding trigger message on work"
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
Generated
+198
-214
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.481.0"
|
||||
version = "1.483.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -32,7 +32,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.481.0"
|
||||
version = "1.483.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -73,7 +73,7 @@ websocket = ["windmill-api/websocket"]
|
||||
http_trigger = ["windmill-api/http_trigger"]
|
||||
postgres_trigger = ["windmill-api/postgres_trigger"]
|
||||
mqtt_trigger = ["windmill-api/mqtt_trigger"]
|
||||
sqs_trigger = ["windmill-api/sqs_trigger"]
|
||||
sqs_trigger = ["windmill-api/sqs_trigger", "windmill-common/aws_auth", "windmill-api/openidconnect"]
|
||||
gcp_trigger = ["windmill-api/gcp_trigger"]
|
||||
smtp = ["windmill-api/smtp", "windmill-common/smtp"]
|
||||
license = ["windmill-api/license"]
|
||||
@@ -304,6 +304,7 @@ postgres-native-tls = "^0"
|
||||
native-tls = "^0"
|
||||
# samael will break compilation on MacOS. Use this fork instead to make it work
|
||||
# samael = { git="https://github.com/njaremko/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = ["xmlsec"] }
|
||||
libxml = { version = "=0.3.3" }
|
||||
samael = { version="0.0.14", features = ["xmlsec"] }
|
||||
gcp_auth = "0.9.0"
|
||||
rust_decimal = { version = "^1", features = ["db-postgres", "serde-float"]}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1475a504133766e005ec551004c2af2283663352
|
||||
b9ca3ccdd188683c81832fd3ed41c8dd632a0c9c
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE app_version DROP COLUMN IF EXISTS raw_app;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE app_version ADD COLUMN IF NOT EXISTS raw_app BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE sqs_trigger DROP COLUMN aws_auth_resource_type;
|
||||
DROP TYPE IF EXISTS AWS_AUTH_RESOURCE_TYPE;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add up migration script here
|
||||
CREATE TYPE AWS_AUTH_RESOURCE_TYPE AS ENUM ('oidc', 'credentials');
|
||||
ALTER TABLE sqs_trigger
|
||||
ADD COLUMN aws_auth_resource_type AWS_AUTH_RESOURCE_TYPE DEFAULT 'credentials'::AWS_AUTH_RESOURCE_TYPE NOT NULL;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE gcp_trigger DROP COLUMN subscription_mode;
|
||||
DROP TYPE GCP_SUBSCRIPTION_MODE;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add up migration script here
|
||||
CREATE TYPE GCP_SUBSCRIPTION_MODE AS ENUM ('create_update', 'existing');
|
||||
ALTER TABLE gcp_trigger ADD COLUMN subscription_mode GCP_SUBSCRIPTION_MODE NOT NULL DEFAULT 'create_update'::GCP_SUBSCRIPTION_MODE;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE websocket_trigger
|
||||
ALTER COLUMN url TYPE VARCHAR(255);
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE websocket_trigger
|
||||
ALTER COLUMN url TYPE VARCHAR(1000);
|
||||
@@ -49,8 +49,12 @@ impl Visit for ImportsFinder {
|
||||
pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
|
||||
let cm: Lrc<SourceMap> = Default::default();
|
||||
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()).into(), code.into());
|
||||
let mut tss = TsSyntax::default();
|
||||
tss.disallow_ambiguous_jsx_like;
|
||||
tss.tsx = true;
|
||||
tss.no_early_errors = true;
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Typescript(TsSyntax::default()),
|
||||
Syntax::Typescript(tss),
|
||||
// EsVersion defaults to es5
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
|
||||
@@ -13,7 +13,7 @@ default = []
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker/enterprise"]
|
||||
stripe = []
|
||||
agent_worker_server = []
|
||||
enterprise_saml = ["dep:samael"]
|
||||
enterprise_saml = ["dep:samael", "dep:libxml"]
|
||||
benchmark = []
|
||||
embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn", "dep:half"]
|
||||
parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet", "windmill-worker/parquet"]
|
||||
@@ -100,6 +100,7 @@ async_zip = { workspace = true, optional = true }
|
||||
regex.workspace = true
|
||||
bytes.workspace = true
|
||||
samael = { workspace = true, optional = true }
|
||||
libxml = { workspace = true, optional = true }
|
||||
async-recursion.workspace = true
|
||||
rsa = { workspace = true, optional = true}
|
||||
uuid.workspace = true
|
||||
@@ -133,6 +134,7 @@ rust-postgres-native-tls = { workspace = true, optional = true}
|
||||
rumqttc = { workspace = true, optional = true }
|
||||
aws-sdk-sqs = { workspace = true, optional = true }
|
||||
aws-config = { workspace = true, optional = true }
|
||||
aws-sdk-sts = { workspace = true, optional = true }
|
||||
google-cloud-pubsub = { workspace = true, optional = true }
|
||||
google-cloud-googleapis = { workspace = true , optional = true }
|
||||
tonic = { workspace = true, optional = true }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.481.0
|
||||
version: 1.483.2
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -5985,6 +5985,55 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/create_raw:
|
||||
post:
|
||||
summary: create app raw
|
||||
operationId: createAppRaw
|
||||
tags:
|
||||
- app
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: new app
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
app:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
value: {}
|
||||
summary:
|
||||
type: string
|
||||
policy:
|
||||
$ref: "#/components/schemas/Policy"
|
||||
draft_only:
|
||||
type: boolean
|
||||
deployment_message:
|
||||
type: string
|
||||
custom_path:
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- value
|
||||
- summary
|
||||
- policy
|
||||
js:
|
||||
type: string
|
||||
css:
|
||||
type: string
|
||||
responses:
|
||||
"201":
|
||||
description: app created
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/exists/{path}:
|
||||
get:
|
||||
summary: does an app exisst at path
|
||||
@@ -6344,6 +6393,49 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/update_raw/{path}:
|
||||
post:
|
||||
summary: update app
|
||||
operationId: updateAppRaw
|
||||
tags:
|
||||
- app
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
requestBody:
|
||||
description: update app
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
app:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
summary:
|
||||
type: string
|
||||
value: {}
|
||||
policy:
|
||||
$ref: "#/components/schemas/Policy"
|
||||
deployment_message:
|
||||
type: string
|
||||
custom_path:
|
||||
type: string
|
||||
js:
|
||||
type: string
|
||||
css:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: app updated
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/custom_path_exists/{custom_path}:
|
||||
get:
|
||||
summary: check if custom path exists
|
||||
@@ -6623,7 +6715,8 @@ paths:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/InputTransform"
|
||||
use_latest_version: boolean
|
||||
use_latest_version:
|
||||
type: boolean
|
||||
flow_options_by_path:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -6633,7 +6726,8 @@ paths:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/InputTransform"
|
||||
use_latest_version: boolean
|
||||
use_latest_version:
|
||||
type: boolean
|
||||
responses:
|
||||
"201":
|
||||
description: stream of created job uuids separated by \n. Lines may start with 'Error:'
|
||||
@@ -9445,7 +9539,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NewGcpTrigger"
|
||||
$ref: "#/components/schemas/GcpTriggerData"
|
||||
responses:
|
||||
"201":
|
||||
description: gcp trigger created
|
||||
@@ -9469,7 +9563,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EditGcpTrigger"
|
||||
$ref: "#/components/schemas/GcpTriggerData"
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger updated
|
||||
@@ -14048,6 +14142,8 @@ components:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
script_hash:
|
||||
type: string
|
||||
args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
language:
|
||||
@@ -14949,8 +15045,6 @@ components:
|
||||
type: string
|
||||
authenticate:
|
||||
type: boolean
|
||||
base_endpoint:
|
||||
type: string
|
||||
required:
|
||||
- authenticate
|
||||
- base_endpoint
|
||||
@@ -14972,6 +15066,8 @@ components:
|
||||
$ref: "#/components/schemas/DeliveryType"
|
||||
delivery_config:
|
||||
$ref: "#/components/schemas/PushConfig"
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/SubscriptionMode"
|
||||
last_server_ping:
|
||||
type: string
|
||||
format: date-time
|
||||
@@ -14985,6 +15081,8 @@ components:
|
||||
- subscription_id
|
||||
- enabled
|
||||
- delivery_type
|
||||
- subscription_mode
|
||||
|
||||
|
||||
SubscriptionMode:
|
||||
type: string
|
||||
@@ -14993,55 +15091,24 @@ components:
|
||||
- create_update
|
||||
description: "The mode of subscription. 'existing' means using an existing GCP subscription, while 'create_update' involves creating or updating a new subscription."
|
||||
|
||||
GcpExistingSubscription:
|
||||
|
||||
GcpTriggerData:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/SubscriptionMode"
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_id:
|
||||
type: string
|
||||
base_endpoint:
|
||||
type: string
|
||||
required:
|
||||
- subscription_id
|
||||
- base_endpoint
|
||||
|
||||
GcpCreateUpdateSubscription:
|
||||
type: object
|
||||
properties:
|
||||
subscription_id:
|
||||
type: string
|
||||
delivery_type:
|
||||
$ref: "#/components/schemas/DeliveryType"
|
||||
delivery_config:
|
||||
$ref: "#/components/schemas/PushConfig"
|
||||
required:
|
||||
- delivery_type
|
||||
|
||||
|
||||
GcpSubscriptionModeConfig:
|
||||
type: object
|
||||
properties:
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/SubscriptionMode"
|
||||
required:
|
||||
- subscription_mode
|
||||
allOf:
|
||||
- oneOf:
|
||||
- $ref: "#/components/schemas/GcpExistingSubscription"
|
||||
- $ref: "#/components/schemas/GcpCreateUpdateSubscription"
|
||||
description: |
|
||||
"This is a union type representing the subscription mode.
|
||||
- 'existing': Represents an existing GCP subscription, and should be accompanied by an 'ExistingGcpSubscription' object.
|
||||
- 'create_update': Represents a new or updated GCP subscription, and should be accompanied by a 'CreateUpdateConfig' object."
|
||||
|
||||
NewGcpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
@@ -15058,34 +15125,6 @@ components:
|
||||
- topic_id
|
||||
- subscription_mode
|
||||
|
||||
EditGcpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
type: string
|
||||
is_flow:
|
||||
type: boolean
|
||||
enabled:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
- is_flow
|
||||
- enabled
|
||||
- mqtt_resource_path
|
||||
- subscription_id
|
||||
- delivery_type
|
||||
- topic_id
|
||||
- subscription_mode
|
||||
|
||||
GetAllTopicSubscription:
|
||||
type: object
|
||||
properties:
|
||||
@@ -15102,6 +15141,12 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- subscription_id
|
||||
|
||||
AwsAuthResourceType:
|
||||
type: string
|
||||
enum:
|
||||
- oidc
|
||||
- credentials
|
||||
|
||||
SqsTrigger:
|
||||
allOf:
|
||||
@@ -15110,6 +15155,8 @@ components:
|
||||
properties:
|
||||
queue_url:
|
||||
type: string
|
||||
aws_auth_resource_type:
|
||||
$ref: "#/components/schemas/AwsAuthResourceType"
|
||||
aws_resource_path:
|
||||
type: string
|
||||
message_attributes:
|
||||
@@ -15130,12 +15177,15 @@ components:
|
||||
- queue_url
|
||||
- aws_resource_path
|
||||
- enabled
|
||||
- aws_auth_resource_type
|
||||
|
||||
NewSqsTrigger:
|
||||
type: object
|
||||
properties:
|
||||
queue_url:
|
||||
type: string
|
||||
aws_auth_resource_type:
|
||||
$ref: "#/components/schemas/AwsAuthResourceType"
|
||||
aws_resource_path:
|
||||
type: string
|
||||
message_attributes:
|
||||
@@ -15156,12 +15206,15 @@ components:
|
||||
- path
|
||||
- script_path
|
||||
- is_flow
|
||||
- aws_auth_resource_type
|
||||
|
||||
EditSqsTrigger:
|
||||
type: object
|
||||
properties:
|
||||
queue_url:
|
||||
type: string
|
||||
aws_auth_resource_type:
|
||||
$ref: "#/components/schemas/AwsAuthResourceType"
|
||||
aws_resource_path:
|
||||
type: string
|
||||
message_attributes:
|
||||
@@ -15183,6 +15236,7 @@ components:
|
||||
- script_path
|
||||
- is_flow
|
||||
- enabled
|
||||
- aws_auth_resource_type
|
||||
|
||||
Slot:
|
||||
type: object
|
||||
@@ -15902,6 +15956,8 @@ components:
|
||||
execution_mode:
|
||||
type: string
|
||||
enum: [viewer, publisher, anonymous]
|
||||
raw_app:
|
||||
type: boolean
|
||||
required:
|
||||
- id
|
||||
- workspace_id
|
||||
|
||||
@@ -24,10 +24,10 @@ use crate::{
|
||||
},
|
||||
users::fetch_api_authed_from_permissioned_as,
|
||||
};
|
||||
#[cfg(feature = "parquet")]
|
||||
use axum::response::Response;
|
||||
use axum::{
|
||||
extract::{Extension, Json, Path, Query},
|
||||
body::Body,
|
||||
extract::{Extension, Json, Multipart, Path, Query},
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, post},
|
||||
Router,
|
||||
@@ -88,10 +88,13 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/get/draft/*path", get(get_app_w_draft))
|
||||
.route("/secret_of/*path", get(get_secret_id))
|
||||
.route("/get/v/*id", get(get_app_by_id))
|
||||
.route("/get_data/v/*id", get(get_raw_app_data))
|
||||
.route("/exists/*path", get(exists_app))
|
||||
.route("/update/*path", post(update_app))
|
||||
.route("/update_raw/*path", post(update_app_raw))
|
||||
.route("/delete/*path", delete(delete_app))
|
||||
.route("/create", post(create_app))
|
||||
.route("/create_raw", post(create_app_raw))
|
||||
.route("/history/p/*path", get(get_app_history))
|
||||
.route("/get_latest_version/*path", get(get_latest_version))
|
||||
.route("/history_update/a/:id/v/:version", post(update_app_history))
|
||||
@@ -135,6 +138,12 @@ pub struct ListableApp {
|
||||
#[sqlx(default)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub deployment_msg: Option<String>,
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
pub raw_app: bool,
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize, Deserialize)]
|
||||
@@ -328,7 +337,8 @@ async fn list_apps(
|
||||
"app.extra_perms",
|
||||
"favorite.path IS NOT NULL as starred",
|
||||
"draft.path IS NOT NULL as has_draft",
|
||||
"draft_only"
|
||||
"draft_only",
|
||||
"app_version.raw_app",
|
||||
])
|
||||
.left()
|
||||
.join("favorite")
|
||||
@@ -387,6 +397,44 @@ async fn list_apps(
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
async fn get_raw_app_data(Path((w_id, version_id)): Path<(String, String)>) -> Result<Response> {
|
||||
let file_path = format!("/tmp/wmill/{}/{}", w_id, version_id);
|
||||
let file = tokio::fs::File::open(file_path).await?;
|
||||
let stream = tokio_util::io::ReaderStream::new(file);
|
||||
let res = Response::builder().header(
|
||||
http::header::CONTENT_TYPE,
|
||||
if version_id.ends_with(".css") {
|
||||
"text/css"
|
||||
} else {
|
||||
"text/javascript"
|
||||
},
|
||||
);
|
||||
Ok(res.body(Body::from_stream(stream)).unwrap())
|
||||
}
|
||||
|
||||
// async fn get_app_version(
|
||||
// authed: ApiAuthed,
|
||||
// Extension(user_db): Extension<UserDB>,
|
||||
// Path((w_id, path)): Path<(String, StripPath)>,
|
||||
// ) -> JsonResult<i64> {
|
||||
// let path = path.to_path();
|
||||
// let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
// let version_o = sqlx::query_scalar!(
|
||||
// "SELECT app.versions[array_upper(app.versions, 1)] as version FROM app
|
||||
// WHERE app.path = $1 AND app.workspace_id = $2",
|
||||
// path,
|
||||
// &w_id,
|
||||
// )
|
||||
// .fetch_optional(&mut *tx)
|
||||
// .await?
|
||||
// .flatten();
|
||||
// tx.commit().await?;
|
||||
|
||||
// let version = not_found_if_none(version_o, "App", path)?;
|
||||
// Ok(Json(version))
|
||||
// }
|
||||
|
||||
async fn get_app(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
@@ -727,6 +775,97 @@ async fn get_secret_id(
|
||||
Ok(hx)
|
||||
}
|
||||
|
||||
macro_rules! process_app_multipart {
|
||||
($authed:expr, $user_db:expr, $db:expr, $w_id:expr, $path:expr, $multipart:expr, $internal_fn:expr) => {
|
||||
async {
|
||||
let mut saved_app = None;
|
||||
let mut uploaded_js = false;
|
||||
|
||||
//todo: use s3 instead
|
||||
let file_path = format!("/tmp/wmill/{}", $w_id);
|
||||
std::fs::create_dir_all(&file_path).unwrap();
|
||||
|
||||
let mut multipart = $multipart;
|
||||
while let Some(field) = multipart.next_field().await.unwrap() {
|
||||
let name = field.name().unwrap().to_string();
|
||||
let data = field.bytes().await.unwrap();
|
||||
if name == "app" {
|
||||
let app = serde_json::from_slice(&data).map_err(to_anyhow)?;
|
||||
let (ntx, npath, nid) = $internal_fn(
|
||||
$authed.clone(),
|
||||
$db.clone(),
|
||||
$user_db.clone(),
|
||||
$w_id,
|
||||
$path,
|
||||
true,
|
||||
app,
|
||||
)
|
||||
.await?;
|
||||
saved_app = Some((npath, nid, ntx));
|
||||
} else if name == "js" {
|
||||
if let Some((_npath, id, _tx)) = saved_app.as_ref() {
|
||||
let file_path = format!("{}/{}.js", file_path, id);
|
||||
std::fs::write(file_path, data).unwrap();
|
||||
uploaded_js = true;
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
"App payload need to be created first".to_string(),
|
||||
));
|
||||
}
|
||||
} else if name == "css" {
|
||||
if let Some((_npath, id, _tx)) = saved_app.as_ref() {
|
||||
let file_path = format!("{}/{}.css", file_path, id);
|
||||
std::fs::write(file_path, data).unwrap();
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
"App payload need to be created first".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::BadRequest(format!("Unsupported field: {}", name)));
|
||||
}
|
||||
}
|
||||
if !uploaded_js {
|
||||
return Err(Error::BadRequest("js or css file not uploaded".to_string()));
|
||||
}
|
||||
if let Some((npath, id, tx)) = saved_app {
|
||||
tx.commit().await?;
|
||||
Ok((npath, id))
|
||||
} else {
|
||||
Err(Error::BadRequest("App not created".to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async fn create_app_raw<'a>(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
multipart: Multipart,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let (path, _id) = process_app_multipart!(
|
||||
authed,
|
||||
user_db,
|
||||
db,
|
||||
&w_id,
|
||||
"",
|
||||
multipart,
|
||||
|authed, db, user_db, w_id, _path, raw_app, app| create_app_internal(
|
||||
authed, db, user_db, w_id, raw_app, app
|
||||
)
|
||||
)
|
||||
.await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateApp { workspace: w_id, path: path.clone() },
|
||||
);
|
||||
Ok((StatusCode::CREATED, path))
|
||||
}
|
||||
|
||||
async fn list_paths_from_workspace_runnable(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
@@ -755,17 +894,36 @@ async fn create_app(
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(mut app): Json<CreateApp>,
|
||||
Json(app): Json<CreateApp>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
let path = app.path.clone();
|
||||
let (new_tx, _path, _id) = create_app_internal(authed, db, user_db, &w_id, false, app).await?;
|
||||
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateApp { workspace: w_id, path: path.clone() },
|
||||
);
|
||||
|
||||
Ok((StatusCode::CREATED, path))
|
||||
}
|
||||
|
||||
async fn create_app_internal<'a>(
|
||||
authed: ApiAuthed,
|
||||
db: sqlx::Pool<sqlx::Postgres>,
|
||||
user_db: UserDB,
|
||||
w_id: &String,
|
||||
raw_app: bool,
|
||||
mut app: CreateApp,
|
||||
) -> Result<(sqlx::Transaction<'a, sqlx::Postgres>, String, i64)> {
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
app.policy.on_behalf_of = Some(username_to_permissioned_as(&authed.username));
|
||||
app.policy.on_behalf_of_email = Some(authed.email.clone());
|
||||
|
||||
let path = app.path.clone();
|
||||
if &app.path == "" {
|
||||
return Err(Error::BadRequest("App path cannot be empty".to_string()));
|
||||
}
|
||||
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM app WHERE path = $1 AND workspace_id = $2)",
|
||||
&app.path,
|
||||
@@ -774,21 +932,19 @@ async fn create_app(
|
||||
.fetch_one(&mut *tx)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
|
||||
if exists {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"App with path {} already exists",
|
||||
&app.path
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(custom_path) = &app.custom_path {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2))",
|
||||
custom_path,
|
||||
if *CLOUD_HOSTED { Some(&w_id) } else { None }
|
||||
if *CLOUD_HOSTED { Some(w_id) } else { None }
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?.unwrap_or(false);
|
||||
@@ -800,7 +956,6 @@ async fn create_app(
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'app'",
|
||||
&app.path,
|
||||
@@ -808,7 +963,6 @@ async fn create_app(
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
let id = sqlx::query_scalar!(
|
||||
"INSERT INTO app
|
||||
(workspace_id, path, summary, policy, versions, draft_only, custom_path)
|
||||
@@ -819,24 +973,24 @@ async fn create_app(
|
||||
json!(app.policy),
|
||||
app.draft_only,
|
||||
app.custom_path
|
||||
.as_ref()
|
||||
.map(|s| if s.is_empty() { None } else { Some(s) })
|
||||
.flatten()
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
let v_id = sqlx::query_scalar!(
|
||||
"INSERT INTO app_version
|
||||
(app_id, value, created_by)
|
||||
VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
(app_id, value, created_by, raw_app)
|
||||
VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
id,
|
||||
//to preserve key orders
|
||||
serde_json::to_string(&app.value).unwrap(),
|
||||
authed.username,
|
||||
raw_app
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE app SET versions = array_append(versions, $1::bigint) WHERE id = $2",
|
||||
v_id,
|
||||
@@ -850,22 +1004,20 @@ async fn create_app(
|
||||
&authed,
|
||||
"apps.create",
|
||||
ActionKind::Create,
|
||||
&w_id,
|
||||
w_id,
|
||||
Some(&app.path),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut args: HashMap<String, Box<serde_json::value::RawValue>> = HashMap::new();
|
||||
if let Some(dm) = app.deployment_message {
|
||||
if let Some(dm) = &app.deployment_message {
|
||||
args.insert("deployment_message".to_string(), to_raw_value(&dm));
|
||||
}
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let (dependency_job_uuid, new_tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
w_id,
|
||||
JobPayload::AppDependencies { path: app.path.clone(), version: v_id },
|
||||
PushArgs { args: &args, extra: None },
|
||||
&authed.username,
|
||||
@@ -889,14 +1041,7 @@ async fn create_app(
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateApp { workspace: w_id, path: app.path.clone() },
|
||||
);
|
||||
|
||||
Ok((StatusCode::CREATED, app.path))
|
||||
Ok((new_tx, path, v_id))
|
||||
}
|
||||
|
||||
async fn list_hub_apps(Extension(db): Extension<DB>) -> impl IntoResponse {
|
||||
@@ -1017,12 +1162,76 @@ async fn update_app(
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(ns): Json<EditApp>,
|
||||
) -> Result<String> {
|
||||
use sql_builder::prelude::*;
|
||||
|
||||
// create_app_internal(authed, user_db, db, &w_id, &mut app).await?;
|
||||
let path = path.to_path();
|
||||
let opath = path.to_string();
|
||||
let (new_tx, npath, _v_id) =
|
||||
update_app_internal(authed, db, user_db, &w_id, path, false, ns).await?;
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateApp {
|
||||
workspace: w_id.clone(),
|
||||
old_path: opath.clone(),
|
||||
new_path: npath.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(format!("app {} updated (npath: {:?})", opath, npath))
|
||||
}
|
||||
|
||||
async fn update_app_raw<'a>(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
multipart: Multipart,
|
||||
) -> Result<String> {
|
||||
let path = path.to_path();
|
||||
let opath = path.to_string();
|
||||
let (npath, _id) = process_app_multipart!(
|
||||
authed,
|
||||
user_db,
|
||||
db,
|
||||
&w_id,
|
||||
path,
|
||||
multipart,
|
||||
update_app_internal
|
||||
)
|
||||
.await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateApp {
|
||||
workspace: w_id.clone(),
|
||||
old_path: opath.to_owned(),
|
||||
new_path: npath.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(format!("app {} updated (npath: {:?})", opath, npath))
|
||||
}
|
||||
// async fn create_app_internal<'a>(
|
||||
// authed: ApiAuthed,
|
||||
// db: sqlx::Pool<sqlx::Postgres>,
|
||||
// user_db: UserDB,
|
||||
// w_id: &String,
|
||||
// app: &mut CreateApp,
|
||||
// )
|
||||
|
||||
async fn update_app_internal<'a>(
|
||||
authed: ApiAuthed,
|
||||
db: sqlx::Pool<sqlx::Postgres>,
|
||||
user_db: UserDB,
|
||||
w_id: &str,
|
||||
path: &str,
|
||||
raw_app: bool,
|
||||
ns: EditApp,
|
||||
) -> Result<(sqlx::Transaction<'a, sqlx::Postgres>, String, i64)> {
|
||||
use sql_builder::prelude::*;
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
|
||||
let npath = if ns.policy.is_some()
|
||||
|| ns.path.is_some()
|
||||
|| ns.summary.is_some()
|
||||
@@ -1069,7 +1278,7 @@ async fn update_app(
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2) AND NOT (path = $3 AND workspace_id = $4))",
|
||||
ncustom_path,
|
||||
if *CLOUD_HOSTED { Some(&w_id) } else { None },
|
||||
if *CLOUD_HOSTED { Some(w_id) } else { None },
|
||||
path,
|
||||
w_id
|
||||
)
|
||||
@@ -1116,12 +1325,13 @@ async fn update_app(
|
||||
|
||||
let v_id = sqlx::query_scalar!(
|
||||
"INSERT INTO app_version
|
||||
(app_id, value, created_by)
|
||||
VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
(app_id, value, created_by, raw_app)
|
||||
VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
app_id,
|
||||
//to preserve key orders
|
||||
serde_json::to_string(&nvalue).unwrap(),
|
||||
authed.username,
|
||||
raw_app
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
@@ -1152,7 +1362,6 @@ async fn update_app(
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'app'",
|
||||
path,
|
||||
@@ -1160,29 +1369,26 @@ async fn update_app(
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"apps.update",
|
||||
ActionKind::Update,
|
||||
&w_id,
|
||||
w_id,
|
||||
Some(&npath),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let mut args: HashMap<String, Box<serde_json::value::RawValue>> = HashMap::new();
|
||||
if let Some(dm) = ns.deployment_message {
|
||||
args.insert("deployment_message".to_string(), to_raw_value(&dm));
|
||||
}
|
||||
args.insert("parent_path".to_string(), to_raw_value(&path));
|
||||
|
||||
let (dependency_job_uuid, new_tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
w_id,
|
||||
JobPayload::AppDependencies { path: npath.clone(), version: v_id },
|
||||
PushArgs { args: &args, extra: None },
|
||||
&authed.username,
|
||||
@@ -1205,18 +1411,7 @@ async fn update_app(
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateApp {
|
||||
workspace: w_id,
|
||||
old_path: path.to_owned(),
|
||||
new_path: npath.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(format!("app {} updated (npath: {:?})", path, npath))
|
||||
Ok((new_tx, npath, v_id))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
|
||||
@@ -18,16 +18,22 @@ use {
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
use crate::gcp_triggers_ee::{
|
||||
manage_google_subscription, process_google_push_request, validate_jwt_token, SubscriptionMode,
|
||||
manage_google_subscription, process_google_push_request, validate_jwt_token,
|
||||
CreateUpdateConfig, SubscriptionMode,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
|
||||
use windmill_common::auth::aws::AwsAuthResourceType;
|
||||
|
||||
#[cfg(any(
|
||||
feature = "http_trigger",
|
||||
all(feature = "enterprise", feature = "gcp_trigger")
|
||||
))]
|
||||
use {
|
||||
axum::extract::Request, http::HeaderMap, serde::de::DeserializeOwned,
|
||||
windmill_common::error::Error,
|
||||
axum::extract::Request,
|
||||
http::HeaderMap,
|
||||
serde::de::DeserializeOwned,
|
||||
windmill_common::{error::Error, utils::empty_string_as_none},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "kafka"))]
|
||||
@@ -150,14 +156,20 @@ pub struct SqsTriggerConfig {
|
||||
pub queue_url: String,
|
||||
pub aws_resource_path: String,
|
||||
pub message_attributes: Option<Vec<String>>,
|
||||
pub aws_auth_resource_type: AwsAuthResourceType,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct GcpTriggerConfig {
|
||||
pub gcp_resource_path: String,
|
||||
#[serde(flatten)]
|
||||
pub subscription_mode: SubscriptionMode,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub subscription_id: Option<String>,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub base_endpoint: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub create_update: Option<CreateUpdateConfig>,
|
||||
pub topic_id: String,
|
||||
}
|
||||
|
||||
@@ -366,11 +378,15 @@ async fn set_gcp_trigger_config(
|
||||
&gcp_config.gcp_resource_path,
|
||||
&capture_config.path,
|
||||
&gcp_config.topic_id,
|
||||
&mut gcp_config.subscription_id,
|
||||
&mut gcp_config.base_endpoint,
|
||||
gcp_config.subscription_mode,
|
||||
gcp_config.create_update,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
gcp_config.subscription_mode = SubscriptionMode::CreateUpdate(config);
|
||||
gcp_config.create_update = Some(config);
|
||||
gcp_config.subscription_mode = SubscriptionMode::CreateUpdate;
|
||||
capture_config.trigger_config = Some(TriggerConfig::Gcp(gcp_config));
|
||||
|
||||
Ok(capture_config)
|
||||
@@ -887,7 +903,7 @@ async fn gcp_payload(
|
||||
|
||||
let authed = fetch_api_authed(owner.clone(), email, &w_id, &db, None).await?;
|
||||
|
||||
let SubscriptionMode::CreateUpdate(config) = &gcp_trigger_config.subscription_mode else {
|
||||
let Some(config) = &gcp_trigger_config.create_update else {
|
||||
return Err(Error::BadConfig("Bad config".to_string()));
|
||||
};
|
||||
|
||||
|
||||
@@ -52,11 +52,12 @@ pub struct ExistingGcpSubscription {
|
||||
pub base_endpoint: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "subscription_mode", rename_all = "snake_case")]
|
||||
#[derive(Debug, Deserialize, Serialize, sqlx::Type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[sqlx(type_name = "GCP_SUBSCRIPTION_MODE", rename_all = "snake_case")]
|
||||
pub enum SubscriptionMode {
|
||||
Existing(ExistingGcpSubscription),
|
||||
CreateUpdate(CreateUpdateConfig),
|
||||
Existing,
|
||||
CreateUpdate,
|
||||
}
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
@@ -77,7 +78,11 @@ pub async fn manage_google_subscription(
|
||||
_gcp_resource_path: &str,
|
||||
_path: &str,
|
||||
_topic_id: &str,
|
||||
_subscription_id: &mut Option<String>,
|
||||
_base_endpoint: &mut Option<String>,
|
||||
_subscription_mode: SubscriptionMode,
|
||||
_create_update_config: Option<CreateUpdateConfig>,
|
||||
_trigger_mode: bool,
|
||||
) -> WindmillResult<CreateUpdateConfig> {
|
||||
Ok(CreateUpdateConfig::default())
|
||||
}
|
||||
@@ -117,6 +122,7 @@ pub struct GcpTrigger {
|
||||
pub subscription_id: String,
|
||||
pub delivery_type: DeliveryType,
|
||||
pub delivery_config: Option<SqlxJson<PushConfig>>,
|
||||
pub subscription_mode: SubscriptionMode,
|
||||
pub topic_id: String,
|
||||
pub path: String,
|
||||
pub script_path: String,
|
||||
|
||||
@@ -253,8 +253,8 @@ mod zoom {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
struct ZoomPayload {
|
||||
#[serde(rename = "plainToken")]
|
||||
plain_token: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -674,14 +674,14 @@ async fn list_selected_job_groups(
|
||||
'kind', jb.kind,
|
||||
'script_path', jb.runnable_path,
|
||||
'latest_schema', COALESCE(
|
||||
(SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),
|
||||
(SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')
|
||||
(SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),
|
||||
(SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow')
|
||||
),
|
||||
'schemas', ARRAY(
|
||||
SELECT jsonb_build_object(
|
||||
'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),
|
||||
'job_ids', ARRAY_AGG(DISTINCT j.id),
|
||||
'schema', ANY_VALUE(COALESCE(s.schema, f.schema))
|
||||
'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]
|
||||
) FROM v2_job j
|
||||
LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'
|
||||
LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'
|
||||
@@ -3095,16 +3095,17 @@ struct CancelJob {
|
||||
enum PreviewKind {
|
||||
Code,
|
||||
Identity,
|
||||
Http,
|
||||
Noop,
|
||||
Bundle,
|
||||
Tarbundle,
|
||||
ScriptHash,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Preview {
|
||||
content: Option<String>,
|
||||
kind: Option<PreviewKind>,
|
||||
script_hash: Option<String>,
|
||||
path: Option<String>,
|
||||
args: Option<HashMap<String, Box<JsonRawValue>>>,
|
||||
language: Option<ScriptLang>,
|
||||
@@ -3440,7 +3441,11 @@ async fn batch_rerun_handle_job(
|
||||
}
|
||||
InputTransform::Javascript { expr } => {
|
||||
#[cfg(not(feature = "deno_core"))]
|
||||
tracing::error!("deno_core feature is not activated, cannot evaluate: {expr}");
|
||||
Err(error::Error::ExecutionErr(
|
||||
format!("deno_core feature is not activated, cannot evaluate: {expr}")
|
||||
.to_string(),
|
||||
))?;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
args.insert(
|
||||
property_name.clone(),
|
||||
@@ -4851,7 +4856,10 @@ async fn run_preview_script(
|
||||
Some(PreviewKind::Identity) => JobPayload::Identity,
|
||||
Some(PreviewKind::Noop) => JobPayload::Noop,
|
||||
_ => JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
hash: preview
|
||||
.script_hash
|
||||
.as_ref()
|
||||
.and_then(|s| windmill_common::scripts::to_i64(s).ok()),
|
||||
content: preview.content.unwrap_or_default(),
|
||||
path: preview.path,
|
||||
language: preview.language.unwrap_or(ScriptLang::Deno),
|
||||
@@ -5831,7 +5839,7 @@ pub fn filter_list_completed_query(
|
||||
if let Some(label) = &lq.label {
|
||||
if lq.allow_wildcards.unwrap_or(false) {
|
||||
let wh = format!(
|
||||
"EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE label LIKE '{}')",
|
||||
"EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE jsonb_typeof(result->'wm_labels') = 'array' AND label LIKE '{}')",
|
||||
&label.replace("*", "%").replace("'", "''")
|
||||
);
|
||||
sqlb.and_where("result ? 'wm_labels'");
|
||||
|
||||
@@ -395,7 +395,7 @@ pub async fn create_postgres_trigger(
|
||||
"Missing replication slot name".to_string(),
|
||||
));
|
||||
}
|
||||
(publication_name.unwrap(), replication_slot_name.unwrap(), )
|
||||
(publication_name.unwrap(), replication_slot_name.unwrap())
|
||||
};
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
@@ -124,11 +124,13 @@ export async function main(
|
||||
transaction_type: "insert" | "update" | "delete",
|
||||
schema_name: string,
|
||||
table_name: string,
|
||||
row: {}
|
||||
row: {},
|
||||
old_row?: {}
|
||||
) {{
|
||||
}}
|
||||
"#,
|
||||
struct_definition
|
||||
&struct_definition,
|
||||
&struct_definition
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ impl RelationConverter {
|
||||
.ok_or(RelationConversionError::FailToFindMatchingTable)
|
||||
}
|
||||
|
||||
pub fn body_to_json(
|
||||
pub fn row_to_json(
|
||||
&self,
|
||||
to_decode: (Oid, Vec<TupleData>),
|
||||
) -> Result<Map<String, Value>, RelationConversionError> {
|
||||
|
||||
@@ -650,37 +650,79 @@ async fn listen_to_transactions(
|
||||
None
|
||||
}
|
||||
Insert(insert) => {
|
||||
Some((insert.o_id, relations.body_to_json((insert.o_id, insert.tuple)), "insert"))
|
||||
Some((insert.o_id, Ok(None), relations.row_to_json((insert.o_id, insert.tuple)), "insert"))
|
||||
}
|
||||
Update(update) => {
|
||||
Some((update.o_id, relations.body_to_json((update.o_id, update.new_tuple)), "update"))
|
||||
let old_row = update.old_tuple.map(|old_tuple| relations.row_to_json((update.o_id, old_tuple))).transpose();
|
||||
let row = relations.row_to_json((update.o_id, update.new_tuple));
|
||||
Some((update.o_id, old_row, row, "update"))
|
||||
}
|
||||
Delete(delete) => {
|
||||
let body = delete.old_tuple.unwrap_or_else(|| delete.key_tuple.unwrap());
|
||||
Some((delete.o_id, relations.body_to_json((delete.o_id, body)), "delete"))
|
||||
let row = delete.old_tuple.unwrap_or_else(|| delete.key_tuple.unwrap());
|
||||
Some((delete.o_id, Ok(None), relations.row_to_json((delete.o_id, row)), "delete"))
|
||||
}
|
||||
};
|
||||
if let Some((o_id, Ok(body), transaction_type)) = json {
|
||||
let relation = match relations.get_relation(o_id) {
|
||||
Ok(relation) => relation,
|
||||
Err(err) => {
|
||||
tracing::error!("Postgres trigger named: {}, error: {}", pg.get_path(), err.to_string());
|
||||
continue;
|
||||
match json {
|
||||
Some((o_id, Ok(old_row), Ok(row), transaction_type)) => {
|
||||
let relation = match relations.get_relation(o_id) {
|
||||
Ok(relation) => relation,
|
||||
Err(err) => {
|
||||
tracing::error!("Postgres trigger named: {}, error: {}", pg.get_path(), err.to_string());
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let database_info = HashMap::from([
|
||||
("schema_name".to_string(), to_raw_value(&relation.namespace)),
|
||||
("table_name".to_string(), to_raw_value(&relation.name)),
|
||||
("transaction_type".to_string(), to_raw_value(&transaction_type)),
|
||||
("old_row".to_string(), to_raw_value(&old_row)),
|
||||
("row".to_string(), to_raw_value(&row)),
|
||||
]);
|
||||
let extra = Some(HashMap::from([(
|
||||
"wm_trigger".to_string(),
|
||||
to_raw_value(&serde_json::json!({"kind": "postgres", })),
|
||||
)]));
|
||||
|
||||
|
||||
let _ = pg.handle(&db, Some(database_info), extra).await;
|
||||
}
|
||||
Some((o_id, old_row, row, transaction_type)) => {
|
||||
let relation = match relations.get_relation(o_id) {
|
||||
Ok(relation) => relation,
|
||||
Err(err) => {
|
||||
tracing::error!("Postgres trigger named: {}, error: {}", pg.get_path(), err.to_string());
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = old_row {
|
||||
tracing::error!(
|
||||
transaction_type = ?transaction_type,
|
||||
schema = %relation.namespace,
|
||||
table = %relation.name,
|
||||
error = %err,
|
||||
"Failed to decode OLD row for {} transaction on {}.{}",
|
||||
transaction_type,
|
||||
relation.namespace,
|
||||
relation.name,
|
||||
);
|
||||
}
|
||||
};
|
||||
let database_info = HashMap::from([
|
||||
("schema_name".to_string(), to_raw_value(&relation.namespace)),
|
||||
("table_name".to_string(), to_raw_value(&relation.name)),
|
||||
("transaction_type".to_string(), to_raw_value(&transaction_type)),
|
||||
("row".to_string(), to_raw_value(&body)),
|
||||
]);
|
||||
let extra = Some(HashMap::from([(
|
||||
"wm_trigger".to_string(),
|
||||
to_raw_value(&serde_json::json!({"kind": "postgres", })),
|
||||
)]));
|
||||
|
||||
|
||||
let _ = pg.handle(&db, Some(database_info), extra).await;
|
||||
|
||||
if let Err(err) = row {
|
||||
tracing::error!(
|
||||
transaction_type = ?transaction_type,
|
||||
schema = %relation.namespace,
|
||||
table = %relation.name,
|
||||
error = %err,
|
||||
"Failed to decode NEW row for {} transaction on {}.{}",
|
||||
transaction_type,
|
||||
relation.namespace,
|
||||
relation.name,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ use std::{
|
||||
};
|
||||
use windmill_audit::audit_ee::audit_log;
|
||||
use windmill_audit::ActionKind;
|
||||
use windmill_worker::process_relative_imports;
|
||||
|
||||
use windmill_common::error::to_anyhow;
|
||||
|
||||
@@ -894,6 +895,38 @@ async fn create_script_internal<'c>(
|
||||
.await?;
|
||||
Ok((hash, new_tx))
|
||||
} else {
|
||||
let db2 = db.clone();
|
||||
let w_id2 = w_id.clone();
|
||||
let authed2 = authed.clone();
|
||||
let permissioned_as2 = permissioned_as.clone();
|
||||
let script_path2 = script_path.clone();
|
||||
let parent_path = p_path_opt.clone();
|
||||
let deployment_message = ns.deployment_message.clone();
|
||||
let content = ns.content.clone();
|
||||
let language = ns.language.clone();
|
||||
tokio::spawn(async move {
|
||||
// wait for 10 seconds to make sure the script is deployed and that the CLI sync that pushed it (f one) is complete
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
if let Err(e) = process_relative_imports(
|
||||
&db2,
|
||||
None,
|
||||
None,
|
||||
&w_id2,
|
||||
&script_path2,
|
||||
parent_path,
|
||||
deployment_message,
|
||||
&content,
|
||||
&Some(language),
|
||||
&authed2.email,
|
||||
&authed2.username,
|
||||
&permissioned_as2,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(%e, "error processing relative imports");
|
||||
}
|
||||
});
|
||||
|
||||
handle_deployment_metadata(
|
||||
&authed.email,
|
||||
&authed.username,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::db::DB;
|
||||
use axum::Router;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use windmill_common::auth::aws::AwsAuthResourceType;
|
||||
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
@@ -14,6 +15,7 @@ pub fn start_sqs(_db: DB, mut _killpill_rx: tokio::sync::broadcast::Receiver<()>
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct SqsTrigger {
|
||||
pub queue_url: String,
|
||||
pub aws_auth_resource_type: AwsAuthResourceType,
|
||||
pub aws_resource_path: String,
|
||||
pub message_attributes: Option<Vec<String>>,
|
||||
pub path: String,
|
||||
|
||||
@@ -501,7 +501,7 @@ pub(crate) async fn tarball_workspace(
|
||||
"SELECT app.id, app.path, app.summary, app.versions, app.policy, app.custom_path,
|
||||
app.extra_perms, app_version.value,
|
||||
app_version.created_at, app_version.created_by from app, app_version
|
||||
WHERE app.workspace_id = $1 AND app_version.id = app.versions[array_upper(app.versions, 1)]",
|
||||
WHERE app.workspace_id = $1 AND app_version.id = app.versions[array_upper(app.versions, 1)] AND app_version.raw_app IS false",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.fetch_all(&mut *tx)
|
||||
@@ -577,13 +577,35 @@ pub(crate) async fn tarball_workspace(
|
||||
#[cfg(feature = "websocket")]
|
||||
{
|
||||
let websocket_triggers = sqlx::query_as!(
|
||||
crate::websocket_triggers::WebsocketTrigger,
|
||||
"SELECT workspace_id, path, url, script_path, is_flow, edited_by, email, edited_at, server_id, last_server_ping, extra_perms, error, enabled, filters as \"filters: _\", initial_messages as \"initial_messages: _\", url_runnable_args as \"url_runnable_args: _\", can_return_message FROM websocket_trigger
|
||||
WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
crate::websocket_triggers::WebsocketTrigger,
|
||||
r#"
|
||||
SELECT
|
||||
workspace_id,
|
||||
path,
|
||||
url,
|
||||
script_path,
|
||||
is_flow,
|
||||
edited_by,
|
||||
email,
|
||||
edited_at,
|
||||
server_id,
|
||||
last_server_ping,
|
||||
extra_perms,
|
||||
error,
|
||||
enabled,
|
||||
filters AS "filters: _",
|
||||
initial_messages AS "initial_messages: _",
|
||||
url_runnable_args AS "url_runnable_args: _",
|
||||
can_return_message
|
||||
FROM
|
||||
websocket_trigger
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
"#,
|
||||
&w_id
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
|
||||
for trigger in websocket_triggers {
|
||||
let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap();
|
||||
@@ -622,8 +644,29 @@ pub(crate) async fn tarball_workspace(
|
||||
{
|
||||
let sqs_triggers = sqlx::query_as!(
|
||||
crate::sqs_triggers_ee::SqsTrigger,
|
||||
"SELECT * FROM sqs_trigger
|
||||
WHERE workspace_id = $1",
|
||||
r#"
|
||||
SELECT
|
||||
aws_auth_resource_type AS "aws_auth_resource_type: _",
|
||||
aws_resource_path,
|
||||
message_attributes,
|
||||
queue_url,
|
||||
workspace_id,
|
||||
path,
|
||||
script_path,
|
||||
is_flow,
|
||||
edited_by,
|
||||
email,
|
||||
edited_at,
|
||||
server_id,
|
||||
last_server_ping,
|
||||
extra_perms,
|
||||
error,
|
||||
enabled
|
||||
FROM
|
||||
sqs_trigger
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
"#,
|
||||
&w_id
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
@@ -649,6 +692,7 @@ pub(crate) async fn tarball_workspace(
|
||||
workspace_id,
|
||||
delivery_type AS "delivery_type: _",
|
||||
delivery_config AS "delivery_config: _",
|
||||
subscription_mode AS "subscription_mode: _",
|
||||
path,
|
||||
script_path,
|
||||
is_flow,
|
||||
|
||||
@@ -13,6 +13,7 @@ prometheus = ["dep:prometheus"]
|
||||
loki = ["dep:tracing-loki"]
|
||||
benchmark = []
|
||||
parquet = ["dep:object_store", "dep:aws-config", "dep:aws-sdk-sts"]
|
||||
aws_auth = ["dep:aws-sdk-sts", "dep:aws-config"]
|
||||
otel = ["dep:opentelemetry-semantic-conventions", "dep:opentelemetry-otlp", "dep:opentelemetry_sdk",
|
||||
"dep:opentelemetry", "dep:tracing-opentelemetry", "dep:opentelemetry-appender-tracing", "dep:tonic"]
|
||||
smtp = ["dep:mail-send"]
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Id in the `app_script` table.
|
||||
@@ -21,3 +23,8 @@ pub struct ListAppQuery {
|
||||
pub include_draft_only: Option<bool>,
|
||||
pub with_deployment_msg: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RawAppValue {
|
||||
pub files: HashMap<String, String>,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use anyhow::Context;
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -10,6 +11,48 @@ use crate::{
|
||||
DB,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IdToken {
|
||||
token: String,
|
||||
expiration: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub fn has_expired(expiration_time: DateTime<Utc>, take: Option<Duration>) -> bool {
|
||||
let now = Utc::now();
|
||||
|
||||
let expiration = match take {
|
||||
Some(duration) => expiration_time - duration,
|
||||
None => expiration_time,
|
||||
};
|
||||
|
||||
now > expiration
|
||||
}
|
||||
|
||||
impl From<IdToken> for String {
|
||||
fn from(value: IdToken) -> Self {
|
||||
value.token
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IdToken {
|
||||
fn to_string(&self) -> String {
|
||||
self.token.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl IdToken {
|
||||
pub fn new(token: String, expiration: DateTime<Utc>) -> Self {
|
||||
Self { token, expiration }
|
||||
}
|
||||
|
||||
pub fn token(&self) -> &str {
|
||||
&self.token
|
||||
}
|
||||
pub fn expiration(&self) -> &DateTime<Utc> {
|
||||
&self.expiration
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct JWTAuthClaims {
|
||||
pub email: String,
|
||||
@@ -267,3 +310,110 @@ pub async fn create_token_for_owner(
|
||||
|
||||
Ok(format!("jwt_{}", token))
|
||||
}
|
||||
|
||||
#[cfg(feature = "aws_auth")]
|
||||
pub mod aws {
|
||||
|
||||
use crate::error::to_anyhow;
|
||||
|
||||
use super::*;
|
||||
use crate::utils::empty_string_as_none;
|
||||
use aws_config::{BehaviorVersion, Region};
|
||||
use aws_sdk_sts::{
|
||||
config::Credentials as AwsCredentials,
|
||||
operation::{
|
||||
assume_role_with_saml::AssumeRoleWithSamlOutput,
|
||||
assume_role_with_web_identity::AssumeRoleWithWebIdentityOutput,
|
||||
},
|
||||
types::Credentials,
|
||||
Client,
|
||||
};
|
||||
|
||||
pub const AWS_OIDC_AUDIENCE: &'static str = "sts.amazonaws.com";
|
||||
|
||||
pub trait GetAuthenticationOutput {
|
||||
fn get_credentials(&self) -> Result<&Credentials>;
|
||||
}
|
||||
|
||||
impl GetAuthenticationOutput for AssumeRoleWithSamlOutput {
|
||||
fn get_credentials(&self) -> Result<&Credentials> {
|
||||
let credentials = self.credentials.as_ref().ok_or(Error::BadGateway(
|
||||
"Error fetching credentials from AWS STS".to_string(),
|
||||
))?;
|
||||
Ok(credentials)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetAuthenticationOutput for AssumeRoleWithWebIdentityOutput {
|
||||
fn get_credentials(&self) -> Result<&Credentials> {
|
||||
let credentials = self.credentials.as_ref().ok_or(Error::BadGateway(
|
||||
"Error fetching credentials from AWS STS".to_string(),
|
||||
))?;
|
||||
Ok(credentials)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)]
|
||||
#[sqlx(type_name = "AWS_AUTH_RESOURCE_TYPE", rename_all = "lowercase")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AwsAuthResourceType {
|
||||
Credentials,
|
||||
Oidc,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CredentialsAuth {
|
||||
#[serde(deserialize_with = "empty_string_as_none")]
|
||||
pub region: Option<String>,
|
||||
#[serde(rename = "awsAccessKeyId")]
|
||||
pub aws_access_key_id: String,
|
||||
#[serde(rename = "awsSecretAccessKey")]
|
||||
pub aws_secret_access_key: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct OidcAuth {
|
||||
#[serde(deserialize_with = "empty_string_as_none")]
|
||||
pub region: Option<String>,
|
||||
#[serde(rename = "roleArn")]
|
||||
pub role_arn: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum AWSAuthConfig {
|
||||
Credentials(CredentialsAuth),
|
||||
Oidc(OidcAuth),
|
||||
}
|
||||
|
||||
pub async fn get_oidc_authentication_data(
|
||||
oidc_auth: OidcAuth,
|
||||
role_session_name: Option<impl ToString>,
|
||||
token: String,
|
||||
) -> Result<AssumeRoleWithWebIdentityOutput> {
|
||||
let region = oidc_auth.region.unwrap_or_else(|| "us-east-1".to_string());
|
||||
|
||||
let credentials = AwsCredentials::new("", "", None, None, "UserInput");
|
||||
|
||||
let config = aws_config::defaults(BehaviorVersion::latest())
|
||||
.credentials_provider(credentials)
|
||||
.region(Region::new(region.clone()))
|
||||
.load()
|
||||
.await;
|
||||
|
||||
let assume_role_with_web_identity_fluent_builder = Client::new(&config)
|
||||
.assume_role_with_web_identity()
|
||||
.set_role_arn(Some(oidc_auth.role_arn))
|
||||
.set_role_session_name(role_session_name.map(|str| str.to_string()))
|
||||
.set_web_identity_token(Some(token));
|
||||
|
||||
let resp = assume_role_with_web_identity_fluent_builder
|
||||
.clone()
|
||||
.send()
|
||||
.await
|
||||
.map_err(to_anyhow)?;
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,3 +476,7 @@ impl CredentialProvider for AwsCredentialAdapter {
|
||||
pub fn bundle(w_id: &str, hash: &str) -> String {
|
||||
format!("script_bundle/{}/{}", w_id, hash)
|
||||
}
|
||||
|
||||
pub fn raw_app(w_id: &str, version: &i64) -> String {
|
||||
format!("/home/rfiszel/raw_app/{}/{}", w_id, version)
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ use git_version::git_version;
|
||||
|
||||
use chrono::Utc;
|
||||
use croner::Cron;
|
||||
use rand::distr::Alphanumeric;
|
||||
use rand::{rng, Rng};
|
||||
use rand::{distr::Alphanumeric, rng, Rng};
|
||||
use reqwest::Client;
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
@@ -4101,6 +4101,7 @@ pub async fn push<'c, 'd>(
|
||||
} else if job_kind == JobKind::Dependencies
|
||||
|| job_kind == JobKind::FlowDependencies
|
||||
|| job_kind == JobKind::DeploymentCallback
|
||||
|| job_kind == JobKind::AppDependencies
|
||||
{
|
||||
// using the dependency tag for deployment callback for now. We can create a separate tag when we need
|
||||
"dependency".to_string()
|
||||
|
||||
@@ -49,6 +49,8 @@ mod worker_flow;
|
||||
mod worker_lockfiles;
|
||||
mod worker_utils;
|
||||
|
||||
pub use worker_lockfiles::process_relative_imports;
|
||||
|
||||
pub use worker::*;
|
||||
|
||||
pub use result_processor::handle_job_error;
|
||||
|
||||
@@ -6,8 +6,10 @@ use itertools::Itertools;
|
||||
use mysql_async::{
|
||||
consts::ColumnType, prelude::*, FromValueError, OptsBuilder, Params, Row, SslOpts,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, value::RawValue, Value};
|
||||
use std::str::FromStr;
|
||||
use tokio::sync::Mutex;
|
||||
use windmill_common::{
|
||||
error::{to_anyhow, Error},
|
||||
@@ -303,26 +305,38 @@ pub async fn do_mysql(
|
||||
return Ok(raw_result);
|
||||
}
|
||||
|
||||
// 2023-12-01T16:18:00.000Z
|
||||
static DATE_REGEX_TZ: Lazy<regex::Regex> = Lazy::new(|| {
|
||||
regex::Regex::new(r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d+)Z").unwrap()
|
||||
});
|
||||
// 2025-04-21 10:08:00
|
||||
static DATE_REGEX: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new(r"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})").unwrap());
|
||||
|
||||
fn string_date_to_mysql_date(s: &str) -> mysql_async::Value {
|
||||
// 2023-12-01T16:18:00.000Z
|
||||
let re = regex::Regex::new(r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d+)Z").unwrap();
|
||||
let caps = re.captures(s);
|
||||
let caps = DATE_REGEX_TZ.captures(s).or_else(|| DATE_REGEX.captures(s));
|
||||
|
||||
if let Some(caps) = caps {
|
||||
mysql_async::Value::Date(
|
||||
caps.get(1).unwrap().as_str().parse().unwrap_or_default(),
|
||||
caps.get(2).unwrap().as_str().parse().unwrap_or_default(),
|
||||
caps.get(3).unwrap().as_str().parse().unwrap_or_default(),
|
||||
caps.get(4).unwrap().as_str().parse().unwrap_or_default(),
|
||||
caps.get(5).unwrap().as_str().parse().unwrap_or_default(),
|
||||
caps.get(6).unwrap().as_str().parse().unwrap_or_default(),
|
||||
caps.get(7).unwrap().as_str().parse().unwrap_or_default(),
|
||||
get_capture_by_index(&caps, 1),
|
||||
get_capture_by_index(&caps, 2),
|
||||
get_capture_by_index(&caps, 3),
|
||||
get_capture_by_index(&caps, 4),
|
||||
get_capture_by_index(&caps, 5),
|
||||
get_capture_by_index(&caps, 6),
|
||||
get_capture_by_index(&caps, 7),
|
||||
)
|
||||
} else {
|
||||
mysql_async::Value::Date(0, 0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_capture_by_index<T: FromStr + Default>(caps: ®ex::Captures, n: usize) -> T {
|
||||
caps.get(n)
|
||||
.and_then(|s| s.as_str().parse::<T>().ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn convert_row_to_value(row: Row) -> serde_json::Value {
|
||||
let mut map = serde_json::Map::new();
|
||||
|
||||
|
||||
@@ -284,6 +284,8 @@ lazy_static::lazy_static! {
|
||||
let mut proxy_env = Vec::new();
|
||||
if let Some(no_proxy) = NO_PROXY.as_ref() {
|
||||
proxy_env.push(("NO_PROXY", no_proxy.to_string()));
|
||||
} else if HTTPS_PROXY.is_some() || HTTP_PROXY.is_some() {
|
||||
proxy_env.push(("NO_PROXY", "localhost,127.0.0.1".to_string()));
|
||||
}
|
||||
if let Some(http_proxy) = HTTP_PROXY.as_ref() {
|
||||
proxy_env.push(("HTTP_PROXY", http_proxy.to_string()));
|
||||
@@ -2457,16 +2459,22 @@ async fn handle_code_execution_job(
|
||||
_ => None,
|
||||
};
|
||||
|
||||
arc_data =
|
||||
preview.ok_or_else(|| Error::internal_err("expected preview".to_string()))?;
|
||||
metadata = ScriptMetadata {
|
||||
language: job.script_lang,
|
||||
codebase,
|
||||
envs: None,
|
||||
schema: None,
|
||||
schema_validator: None,
|
||||
};
|
||||
(arc_data.as_ref(), &metadata)
|
||||
if codebase.is_none() && job.runnable_id.is_some() {
|
||||
(arc_data, arc_metadata) =
|
||||
cache::script::fetch(conn, job.runnable_id.unwrap()).await?;
|
||||
(arc_data.as_ref(), arc_metadata.as_ref())
|
||||
} else {
|
||||
arc_data =
|
||||
preview.ok_or_else(|| Error::internal_err("expected preview".to_string()))?;
|
||||
metadata = ScriptMetadata {
|
||||
language: job.script_lang,
|
||||
codebase,
|
||||
envs: None,
|
||||
schema: None,
|
||||
schema_validator: None,
|
||||
};
|
||||
(arc_data.as_ref(), &metadata)
|
||||
}
|
||||
}
|
||||
JobKind::Script_Hub => {
|
||||
let ContentReqLangEnvs { content, lockfile, language, envs, codebase, schema } =
|
||||
|
||||
@@ -368,44 +368,21 @@ pub async fn handle_dependency_job(
|
||||
tracing::error!(%e, "error handling deployment metadata");
|
||||
}
|
||||
|
||||
let relative_imports =
|
||||
extract_relative_imports(&script_data.code, script_path, &job.script_lang);
|
||||
if let Some(relative_imports) = relative_imports {
|
||||
update_script_dependency_map(
|
||||
&job.id,
|
||||
db,
|
||||
w_id,
|
||||
&parent_path,
|
||||
script_path,
|
||||
relative_imports,
|
||||
)
|
||||
.await?;
|
||||
let already_visited = job
|
||||
.args
|
||||
.as_ref()
|
||||
.map(|x| {
|
||||
x.get("already_visited")
|
||||
.map(|v| serde_json::from_str::<Vec<String>>(v.get()).ok())
|
||||
.flatten()
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or_default();
|
||||
if let Err(e) = trigger_dependents_to_recompute_dependencies(
|
||||
w_id,
|
||||
script_path,
|
||||
deployment_message,
|
||||
parent_path,
|
||||
&job.permissioned_as_email,
|
||||
&job.created_by,
|
||||
&job.permissioned_as,
|
||||
db,
|
||||
already_visited,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(%e, "error triggering dependents to recompute dependencies");
|
||||
}
|
||||
}
|
||||
process_relative_imports(
|
||||
db,
|
||||
Some(job.id),
|
||||
job.args.as_ref(),
|
||||
&job.workspace_id,
|
||||
script_path,
|
||||
parent_path,
|
||||
deployment_message,
|
||||
&script_data.code,
|
||||
&job.script_lang,
|
||||
&job.permissioned_as_email,
|
||||
&job.created_by,
|
||||
&job.permissioned_as,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(to_raw_value_owned(
|
||||
json!({ "status": "Successful lock file generation", "lock": content }),
|
||||
@@ -443,6 +420,58 @@ fn remove_ansi_codes(s: &str) -> String {
|
||||
ANSI_REGEX.replace_all(s, "").to_string()
|
||||
}
|
||||
|
||||
pub async fn process_relative_imports(
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
job_id: Option<Uuid>,
|
||||
args: Option<&Json<HashMap<String, Box<RawValue>>>>,
|
||||
w_id: &str,
|
||||
script_path: &str,
|
||||
parent_path: Option<String>,
|
||||
deployment_message: Option<String>,
|
||||
code: &str,
|
||||
script_lang: &Option<ScriptLang>,
|
||||
permissioned_as_email: &str,
|
||||
created_by: &str,
|
||||
permissioned_as: &str,
|
||||
) -> error::Result<()> {
|
||||
let relative_imports = extract_relative_imports(&code, script_path, script_lang);
|
||||
if let Some(relative_imports) = relative_imports {
|
||||
update_script_dependency_map(
|
||||
&job_id.unwrap_or_else(|| Uuid::nil()),
|
||||
db,
|
||||
w_id,
|
||||
&parent_path,
|
||||
script_path,
|
||||
relative_imports,
|
||||
)
|
||||
.await?;
|
||||
let already_visited = args
|
||||
.map(|x| {
|
||||
x.get("already_visited")
|
||||
.map(|v| serde_json::from_str::<Vec<String>>(v.get()).ok())
|
||||
.flatten()
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or_default();
|
||||
if let Err(e) = trigger_dependents_to_recompute_dependencies(
|
||||
w_id,
|
||||
script_path,
|
||||
deployment_message,
|
||||
parent_path,
|
||||
permissioned_as_email,
|
||||
created_by,
|
||||
permissioned_as,
|
||||
db,
|
||||
already_visited,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(%e, "error triggering dependents to recompute dependencies");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn trigger_dependents_to_recompute_dependencies(
|
||||
w_id: &str,
|
||||
script_path: &str,
|
||||
@@ -1735,6 +1764,90 @@ pub async fn handle_app_dependency_job(
|
||||
}
|
||||
}
|
||||
|
||||
// async fn upload_raw_app(
|
||||
// app_value: &RawAppValue,
|
||||
// job: &QueuedJob,
|
||||
// mem_peak: &mut i32,
|
||||
// canceled_by: &mut Option<CanceledBy>,
|
||||
// job_dir: &str,
|
||||
// db: &sqlx::Pool<sqlx::Postgres>,
|
||||
// worker_name: &str,
|
||||
// occupancy_metrics: &mut Option<&mut OccupancyMetrics>,
|
||||
// version: i64,
|
||||
// ) -> Result<()> {
|
||||
// let mut entrypoint = "index.ts";
|
||||
// for file in app_value.files.iter() {
|
||||
// if file.0 == "/index.tsx" {
|
||||
// entrypoint = "index.tsx";
|
||||
// } else if file.0 == "/index.js" {
|
||||
// entrypoint = "index.js";
|
||||
// }
|
||||
// write_file(&job_dir, file.0, &file.1)?;
|
||||
// }
|
||||
// let common_bun_proc_envs: HashMap<String, String> = get_common_bun_proc_envs(None).await;
|
||||
|
||||
// install_bun_lockfile(
|
||||
// mem_peak,
|
||||
// canceled_by,
|
||||
// &job.id,
|
||||
// &job.workspace_id,
|
||||
// Some(db),
|
||||
// job_dir,
|
||||
// worker_name,
|
||||
// common_bun_proc_envs,
|
||||
// false,
|
||||
// occupancy_metrics,
|
||||
// )
|
||||
// .await?;
|
||||
// let mut cmd = tokio::process::Command::new("esbuild");
|
||||
// let mut args = "--bundle --minify --outdir=dist/"
|
||||
// .split(' ')
|
||||
// .collect::<Vec<_>>();
|
||||
// args.push(entrypoint);
|
||||
// cmd.current_dir(job_dir)
|
||||
// .env_clear()
|
||||
// .args(args)
|
||||
// .stdout(Stdio::piped())
|
||||
// .stderr(Stdio::piped());
|
||||
// let child = start_child_process(cmd, "esbuild").await?;
|
||||
|
||||
// crate::handle_child::handle_child(
|
||||
// &job.id,
|
||||
// db,
|
||||
// mem_peak,
|
||||
// canceled_by,
|
||||
// child,
|
||||
// false,
|
||||
// worker_name,
|
||||
// &job.workspace_id,
|
||||
// "esbuild",
|
||||
// Some(30),
|
||||
// false,
|
||||
// occupancy_metrics,
|
||||
// )
|
||||
// .await?;
|
||||
// let output_dir = format!("{}/dist", job_dir);
|
||||
// let target_dir = format!("/home/rfiszel/wmill/{}/{}", job.workspace_id, version);
|
||||
|
||||
// tokio::fs::create_dir_all(&target_dir).await?;
|
||||
|
||||
// tracing::info!("Copying files from {} to {}", output_dir, target_dir);
|
||||
|
||||
// let index_ts = format!("{}/index.js", output_dir);
|
||||
// let index_css = format!("{}/index.css", output_dir);
|
||||
|
||||
// if tokio::fs::metadata(&index_ts).await.is_ok() {
|
||||
// tokio::fs::copy(&index_ts, format!("{}/index.js", target_dir)).await?;
|
||||
// }
|
||||
|
||||
// if tokio::fs::metadata(&index_css).await.is_ok() {
|
||||
// tokio::fs::copy(&index_css, format!("{}/index.css", target_dir)).await?;
|
||||
// }
|
||||
// // let file_path = format!("/home/rfiszel/wmill/{}/{}", job.workspace_id, version);
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
async fn python_dep(
|
||||
reqs: String,
|
||||
|
||||
+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.481.0";
|
||||
export const VERSION = "v1.483.2";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
npm/
|
||||
gen/
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions.ts';
|
||||
import type { ApiResult } from './ApiResult.ts';
|
||||
|
||||
export class ApiError extends Error {
|
||||
public readonly url: string;
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
public readonly body: unknown;
|
||||
public readonly request: ApiRequestOptions;
|
||||
|
||||
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
|
||||
super(message);
|
||||
|
||||
this.name = 'ApiError';
|
||||
this.url = response.url;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
this.body = response.body;
|
||||
this.request = request;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
export type ApiRequestOptions<T = unknown> = {
|
||||
readonly body?: any;
|
||||
readonly cookies?: Record<string, unknown>;
|
||||
readonly errors?: Record<number | string, string>;
|
||||
readonly formData?: Record<string, unknown> | any[] | Blob | File;
|
||||
readonly headers?: Record<string, unknown>;
|
||||
readonly mediaType?: string;
|
||||
readonly method:
|
||||
| 'DELETE'
|
||||
| 'GET'
|
||||
| 'HEAD'
|
||||
| 'OPTIONS'
|
||||
| 'PATCH'
|
||||
| 'POST'
|
||||
| 'PUT';
|
||||
readonly path?: Record<string, unknown>;
|
||||
readonly query?: Record<string, unknown>;
|
||||
readonly responseHeader?: string;
|
||||
readonly responseTransformer?: (data: unknown) => Promise<T>;
|
||||
readonly url: string;
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
export type ApiResult<TData = any> = {
|
||||
readonly body: TData;
|
||||
readonly ok: boolean;
|
||||
readonly status: number;
|
||||
readonly statusText: string;
|
||||
readonly url: string;
|
||||
};
|
||||
@@ -1,126 +0,0 @@
|
||||
export class CancelError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'CancelError';
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnCancel {
|
||||
readonly isResolved: boolean;
|
||||
readonly isRejected: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
|
||||
(cancelHandler: () => void): void;
|
||||
}
|
||||
|
||||
export class CancelablePromise<T> implements Promise<T> {
|
||||
private _isResolved: boolean;
|
||||
private _isRejected: boolean;
|
||||
private _isCancelled: boolean;
|
||||
readonly cancelHandlers: (() => void)[];
|
||||
readonly promise: Promise<T>;
|
||||
private _resolve?: (value: T | PromiseLike<T>) => void;
|
||||
private _reject?: (reason?: unknown) => void;
|
||||
|
||||
constructor(
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason?: unknown) => void,
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this._isResolved = false;
|
||||
this._isRejected = false;
|
||||
this._isCancelled = false;
|
||||
this.cancelHandlers = [];
|
||||
this.promise = new Promise<T>((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this._isResolved = true;
|
||||
if (this._resolve) this._resolve(value);
|
||||
};
|
||||
|
||||
const onReject = (reason?: unknown): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this._isRejected = true;
|
||||
if (this._reject) this._reject(reason);
|
||||
};
|
||||
|
||||
const onCancel = (cancelHandler: () => void): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.cancelHandlers.push(cancelHandler);
|
||||
};
|
||||
|
||||
Object.defineProperty(onCancel, 'isResolved', {
|
||||
get: (): boolean => this._isResolved,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isRejected', {
|
||||
get: (): boolean => this._isRejected,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this._isCancelled,
|
||||
});
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
});
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "Cancellable Promise";
|
||||
}
|
||||
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.promise.then(onFulfilled, onRejected);
|
||||
}
|
||||
|
||||
public catch<TResult = never>(
|
||||
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
|
||||
): Promise<T | TResult> {
|
||||
return this.promise.catch(onRejected);
|
||||
}
|
||||
|
||||
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this.promise.finally(onFinally);
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this._isCancelled = true;
|
||||
if (this.cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.cancelHandlers) {
|
||||
cancelHandler();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Cancellation threw an error', error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.cancelHandlers.length = 0;
|
||||
if (this._reject) this._reject(new CancelError('Request aborted'));
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return this._isCancelled;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
const getEnv = (key: string) => {
|
||||
return Deno.env.get(key)
|
||||
};
|
||||
|
||||
const baseUrl = getEnv("BASE_INTERNAL_URL") ?? getEnv("BASE_URL") ?? "http://localhost:8000";
|
||||
const baseUrlApi = (baseUrl ?? '') + "/api";
|
||||
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions.ts';
|
||||
|
||||
type Headers = Record<string, string>;
|
||||
type Middleware<T> = (value: T) => T | Promise<T>;
|
||||
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
||||
|
||||
export class Interceptors<T> {
|
||||
_fns: Middleware<T>[];
|
||||
|
||||
constructor() {
|
||||
this._fns = [];
|
||||
}
|
||||
|
||||
eject(fn: Middleware<T>): void {
|
||||
const index = this._fns.indexOf(fn);
|
||||
if (index !== -1) {
|
||||
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
|
||||
}
|
||||
}
|
||||
|
||||
use(fn: Middleware<T>): void {
|
||||
this._fns = [...this._fns, fn];
|
||||
}
|
||||
}
|
||||
|
||||
export type OpenAPIConfig = {
|
||||
BASE: string;
|
||||
CREDENTIALS: 'include' | 'omit' | 'same-origin';
|
||||
ENCODE_PATH?: ((path: string) => string) | undefined;
|
||||
HEADERS?: Headers | Resolver<Headers> | undefined;
|
||||
PASSWORD?: string | Resolver<string> | undefined;
|
||||
TOKEN?: string | Resolver<string> | undefined;
|
||||
USERNAME?: string | Resolver<string> | undefined;
|
||||
VERSION: string;
|
||||
WITH_CREDENTIALS: boolean;
|
||||
interceptors: {
|
||||
request: Interceptors<RequestInit>;
|
||||
response: Interceptors<Response>;
|
||||
};
|
||||
};
|
||||
|
||||
export const OpenAPI: OpenAPIConfig = {
|
||||
BASE: baseUrlApi,
|
||||
CREDENTIALS: 'include',
|
||||
ENCODE_PATH: undefined,
|
||||
HEADERS: undefined,
|
||||
PASSWORD: undefined,
|
||||
TOKEN: getEnv("WM_TOKEN"),
|
||||
USERNAME: undefined,
|
||||
VERSION: '1.481.0',
|
||||
WITH_CREDENTIALS: true,
|
||||
interceptors: {
|
||||
request: new Interceptors(),
|
||||
response: new Interceptors(),
|
||||
},
|
||||
};
|
||||
@@ -1,350 +0,0 @@
|
||||
import { ApiError } from './ApiError.ts';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions.ts';
|
||||
import type { ApiResult } from './ApiResult.ts';
|
||||
import { CancelablePromise } from './CancelablePromise.ts';
|
||||
import type { OnCancel } from './CancelablePromise.ts';
|
||||
import type { OpenAPIConfig } from './OpenAPI.ts';
|
||||
|
||||
export const isString = (value: unknown): value is string => {
|
||||
return typeof value === 'string';
|
||||
};
|
||||
|
||||
export const isStringWithValue = (value: unknown): value is string => {
|
||||
return isString(value) && value !== '';
|
||||
};
|
||||
|
||||
export const isBlob = (value: any): value is Blob => {
|
||||
return value instanceof Blob;
|
||||
};
|
||||
|
||||
export const isFormData = (value: unknown): value is FormData => {
|
||||
return value instanceof FormData;
|
||||
};
|
||||
|
||||
export const base64 = (str: string): string => {
|
||||
try {
|
||||
return btoa(str);
|
||||
} catch (err) {
|
||||
// @ts-ignore
|
||||
return Buffer.from(str).toString('base64');
|
||||
}
|
||||
};
|
||||
|
||||
export const getQueryString = (params: Record<string, unknown>): string => {
|
||||
const qs: string[] = [];
|
||||
|
||||
const append = (key: string, value: unknown) => {
|
||||
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
||||
};
|
||||
|
||||
const encodePair = (key: string, value: unknown) => {
|
||||
if (value === undefined || value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof Date) {
|
||||
append(key, value.toISOString());
|
||||
} else if (Array.isArray(value)) {
|
||||
value.forEach(v => encodePair(key, v));
|
||||
} else if (typeof value === 'object') {
|
||||
Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v));
|
||||
} else {
|
||||
append(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => encodePair(key, value));
|
||||
|
||||
return qs.length ? `?${qs.join('&')}` : '';
|
||||
};
|
||||
|
||||
const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
|
||||
const encoder = config.ENCODE_PATH || encodeURI;
|
||||
|
||||
const path = options.url
|
||||
.replace('{api-version}', config.VERSION)
|
||||
.replace(/{(.*?)}/g, (substring: string, group: string) => {
|
||||
if (options.path?.hasOwnProperty(group)) {
|
||||
return encoder(String(options.path[group]));
|
||||
}
|
||||
return substring;
|
||||
});
|
||||
|
||||
const url = config.BASE + path;
|
||||
return options.query ? url + getQueryString(options.query) : url;
|
||||
};
|
||||
|
||||
export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
|
||||
if (options.formData) {
|
||||
const formData = new FormData();
|
||||
|
||||
const process = (key: string, value: unknown) => {
|
||||
if (isString(value) || isBlob(value)) {
|
||||
formData.append(key, value);
|
||||
} else {
|
||||
formData.append(key, JSON.stringify(value));
|
||||
}
|
||||
};
|
||||
|
||||
Object.entries(options.formData)
|
||||
.filter(([, value]) => value !== undefined && value !== null)
|
||||
.forEach(([key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(v => process(key, v));
|
||||
} else {
|
||||
process(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return formData;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
||||
|
||||
export const resolve = async <T>(options: ApiRequestOptions<T>, resolver?: T | Resolver<T>): Promise<T | undefined> => {
|
||||
if (typeof resolver === 'function') {
|
||||
return (resolver as Resolver<T>)(options);
|
||||
}
|
||||
return resolver;
|
||||
};
|
||||
|
||||
export const getHeaders = async <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>): Promise<Headers> => {
|
||||
const [token, username, password, additionalHeaders] = await Promise.all([
|
||||
// @ts-ignore
|
||||
resolve(options, config.TOKEN),
|
||||
// @ts-ignore
|
||||
resolve(options, config.USERNAME),
|
||||
// @ts-ignore
|
||||
resolve(options, config.PASSWORD),
|
||||
// @ts-ignore
|
||||
resolve(options, config.HEADERS),
|
||||
]);
|
||||
|
||||
const headers = Object.entries({
|
||||
Accept: 'application/json',
|
||||
...additionalHeaders,
|
||||
...options.headers,
|
||||
})
|
||||
.filter(([, value]) => value !== undefined && value !== null)
|
||||
.reduce((headers, [key, value]) => ({
|
||||
...headers,
|
||||
[key]: String(value),
|
||||
}), {} as Record<string, string>);
|
||||
|
||||
if (isStringWithValue(token)) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||
const credentials = base64(`${username}:${password}`);
|
||||
headers['Authorization'] = `Basic ${credentials}`;
|
||||
}
|
||||
|
||||
if (options.body !== undefined) {
|
||||
if (options.mediaType) {
|
||||
headers['Content-Type'] = options.mediaType;
|
||||
} else if (isBlob(options.body)) {
|
||||
headers['Content-Type'] = options.body.type || 'application/octet-stream';
|
||||
} else if (isString(options.body)) {
|
||||
headers['Content-Type'] = 'text/plain';
|
||||
} else if (!isFormData(options.body)) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
}
|
||||
|
||||
return new Headers(headers);
|
||||
};
|
||||
|
||||
export const getRequestBody = (options: ApiRequestOptions): unknown => {
|
||||
if (options.body !== undefined) {
|
||||
if (options.mediaType?.includes('application/json') || options.mediaType?.includes('+json')) {
|
||||
return JSON.stringify(options.body);
|
||||
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
|
||||
return options.body;
|
||||
} else {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const sendRequest = async (
|
||||
config: OpenAPIConfig,
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
body: any,
|
||||
formData: FormData | undefined,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<Response> => {
|
||||
const controller = new AbortController();
|
||||
|
||||
let request: RequestInit = {
|
||||
headers,
|
||||
body: body ?? formData,
|
||||
method: options.method,
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
if (config.WITH_CREDENTIALS) {
|
||||
request.credentials = config.CREDENTIALS;
|
||||
}
|
||||
|
||||
for (const fn of config.interceptors.request._fns) {
|
||||
request = await fn(request);
|
||||
}
|
||||
|
||||
onCancel(() => controller.abort());
|
||||
|
||||
return await fetch(url, request);
|
||||
};
|
||||
|
||||
export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getResponseBody = async (response: Response): Promise<unknown> => {
|
||||
if (response.status !== 204) {
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
const binaryTypes = ['application/octet-stream', 'application/pdf', 'application/zip', 'audio/', 'image/', 'video/'];
|
||||
if (contentType.includes('application/json') || contentType.includes('+json')) {
|
||||
return await response.json();
|
||||
} else if (binaryTypes.some(type => contentType.includes(type))) {
|
||||
return await response.blob();
|
||||
} else if (contentType.includes('multipart/form-data')) {
|
||||
return await response.formData();
|
||||
} else if (contentType.includes('text/')) {
|
||||
return await response.text();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
|
||||
const errors: Record<number, string> = {
|
||||
400: 'Bad Request',
|
||||
401: 'Unauthorized',
|
||||
402: 'Payment Required',
|
||||
403: 'Forbidden',
|
||||
404: 'Not Found',
|
||||
405: 'Method Not Allowed',
|
||||
406: 'Not Acceptable',
|
||||
407: 'Proxy Authentication Required',
|
||||
408: 'Request Timeout',
|
||||
409: 'Conflict',
|
||||
410: 'Gone',
|
||||
411: 'Length Required',
|
||||
412: 'Precondition Failed',
|
||||
413: 'Payload Too Large',
|
||||
414: 'URI Too Long',
|
||||
415: 'Unsupported Media Type',
|
||||
416: 'Range Not Satisfiable',
|
||||
417: 'Expectation Failed',
|
||||
418: 'Im a teapot',
|
||||
421: 'Misdirected Request',
|
||||
422: 'Unprocessable Content',
|
||||
423: 'Locked',
|
||||
424: 'Failed Dependency',
|
||||
425: 'Too Early',
|
||||
426: 'Upgrade Required',
|
||||
428: 'Precondition Required',
|
||||
429: 'Too Many Requests',
|
||||
431: 'Request Header Fields Too Large',
|
||||
451: 'Unavailable For Legal Reasons',
|
||||
500: 'Internal Server Error',
|
||||
501: 'Not Implemented',
|
||||
502: 'Bad Gateway',
|
||||
503: 'Service Unavailable',
|
||||
504: 'Gateway Timeout',
|
||||
505: 'HTTP Version Not Supported',
|
||||
506: 'Variant Also Negotiates',
|
||||
507: 'Insufficient Storage',
|
||||
508: 'Loop Detected',
|
||||
510: 'Not Extended',
|
||||
511: 'Network Authentication Required',
|
||||
...options.errors,
|
||||
}
|
||||
|
||||
const error = errors[result.status];
|
||||
if (error) {
|
||||
throw new ApiError(options, result, error);
|
||||
}
|
||||
|
||||
if (!result.ok) {
|
||||
const errorStatus = result.status ?? 'unknown';
|
||||
const errorStatusText = result.statusText ?? 'unknown';
|
||||
const errorBody = (() => {
|
||||
try {
|
||||
return JSON.stringify(result.body, null, 2);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
throw new ApiError(options, result,
|
||||
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Request method
|
||||
* @param config The OpenAPI configuration object
|
||||
* @param options The request options from the service
|
||||
* @returns CancelablePromise<T>
|
||||
* @throws ApiError
|
||||
*/
|
||||
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>): CancelablePromise<T> => {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(config, options);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(config, options);
|
||||
|
||||
if (!onCancel.isCancelled) {
|
||||
let response = await sendRequest(config, options, url, body, formData, headers, onCancel);
|
||||
|
||||
for (const fn of config.interceptors.response._fns) {
|
||||
response = await fn(response);
|
||||
}
|
||||
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
let transformedBody = responseBody;
|
||||
if (options.responseTransformer && response.ok) {
|
||||
transformedBody = await options.responseTransformer(responseBody)
|
||||
}
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader ?? transformedBody,
|
||||
};
|
||||
|
||||
catchErrorCodes(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
export { ApiError } from './core/ApiError.ts';
|
||||
export { CancelablePromise, CancelError } from './core/CancelablePromise.ts';
|
||||
export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI.ts';
|
||||
export * from './services.gen.ts';
|
||||
export * from './types.gen.ts';
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -63,7 +63,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.481.0";
|
||||
export const VERSION = "1.483.2";
|
||||
|
||||
const command = new Command()
|
||||
.name("wmill")
|
||||
|
||||
+21
-8
@@ -28,7 +28,7 @@ type Trigger = {
|
||||
postgres: PostgresTrigger;
|
||||
mqtt: MqttTrigger;
|
||||
sqs: SqsTrigger;
|
||||
gcp: GcpTrigger
|
||||
gcp: GcpTrigger;
|
||||
};
|
||||
|
||||
type TriggerFile<K extends TriggerType> = Omit<
|
||||
@@ -62,7 +62,7 @@ async function getTrigger<K extends TriggerType>(
|
||||
postgres: wmill.getPostgresTrigger,
|
||||
mqtt: wmill.getMqttTrigger,
|
||||
sqs: wmill.getSqsTrigger,
|
||||
gcp: wmill.getGcpTrigger
|
||||
gcp: wmill.getGcpTrigger,
|
||||
};
|
||||
const triggerFunction = triggerFunctions[triggerType];
|
||||
|
||||
@@ -90,7 +90,9 @@ async function updateTrigger<K extends TriggerType>(
|
||||
postgres: wmill.updatePostgresTrigger,
|
||||
mqtt: wmill.updateMqttTrigger,
|
||||
sqs: wmill.updateSqsTrigger,
|
||||
gcp: wmill.updateGcpTrigger
|
||||
gcp: async (args) => {
|
||||
throw new Error("GCP triggers are not supported yet");
|
||||
},
|
||||
};
|
||||
const triggerFunction = triggerFunctions[triggerType];
|
||||
await triggerFunction({ workspace, path, requestBody: trigger });
|
||||
@@ -116,7 +118,9 @@ async function createTrigger<K extends TriggerType>(
|
||||
postgres: wmill.createPostgresTrigger,
|
||||
mqtt: wmill.createMqttTrigger,
|
||||
sqs: wmill.createSqsTrigger,
|
||||
gcp: wmill.createGcpTrigger
|
||||
gcp: async (args) => {
|
||||
throw new Error("GCP triggers are not supported yet");
|
||||
},
|
||||
};
|
||||
const triggerFunction = triggerFunctions[triggerType];
|
||||
await triggerFunction({ workspace, path, requestBody: trigger });
|
||||
@@ -197,8 +201,8 @@ async function list(opts: GlobalOptions) {
|
||||
workspace: workspace.workspaceId,
|
||||
});
|
||||
const gcpTriggers = await wmill.listGcpTriggers({
|
||||
workspace: workspace.workspaceId
|
||||
})
|
||||
workspace: workspace.workspaceId,
|
||||
});
|
||||
const triggers = [
|
||||
...httpTriggers.map((x) => ({ path: x.path, kind: "http" })),
|
||||
...websocketTriggers.map((x) => ({ path: x.path, kind: "websocket" })),
|
||||
@@ -207,7 +211,7 @@ async function list(opts: GlobalOptions) {
|
||||
...postgresTriggers.map((x) => ({ path: x.path, kind: "postgres" })),
|
||||
...mqttTriggers.map((x) => ({ path: x.path, kind: "mqtt" })),
|
||||
...sqsTriggers.map((x) => ({ path: x.path, kind: "sqs" })),
|
||||
...gcpTriggers.map((x) => ({ path: x.path, kind: "gcp" }))
|
||||
...gcpTriggers.map((x) => ({ path: x.path, kind: "gcp" })),
|
||||
];
|
||||
|
||||
new Table()
|
||||
@@ -221,7 +225,16 @@ async function list(opts: GlobalOptions) {
|
||||
function checkIfValidTrigger(kind: string | undefined): kind is TriggerType {
|
||||
if (
|
||||
kind &&
|
||||
["http", "websocket", "kafka", "nats", "postgres", "mqtt", "sqs", "gcp"].includes(kind)
|
||||
[
|
||||
"http",
|
||||
"websocket",
|
||||
"kafka",
|
||||
"nats",
|
||||
"postgres",
|
||||
"mqtt",
|
||||
"sqs",
|
||||
"gcp",
|
||||
].includes(kind)
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# windmill-cloudformation
|
||||
Cloudformation Template for Windmill on AWS EKS
|
||||
|
||||
## Overview
|
||||
|
||||
This CloudFormation template automatically deploys Windmill on AWS EKS. The deployment includes:
|
||||
|
||||
- An EKS cluster with configurable node types and sizes
|
||||
- An RDS PostgreSQL database for Windmill data
|
||||
- AWS Load Balancer Controller for handling ingress traffic
|
||||
- Proper network configuration with VPC, subnets, and security groups
|
||||
- A fully automated installation of Windmill via Helm
|
||||
|
||||
## Parameters
|
||||
|
||||
The template accepts various parameters to customize your deployment:
|
||||
|
||||
- **NodeInstanceType**: EC2 instance type for EKS worker nodes (t3.small to r5.2xlarge)
|
||||
- **NodeGroupSize**: Number of EKS worker nodes
|
||||
- **RdsInstanceClass**: RDS instance class for the PostgreSQL database (db.t3.micro to db.r5.2xlarge)
|
||||
- **DBPassword**: Password for the PostgreSQL database
|
||||
- **WorkerReplicas**: Number of Windmill worker replicas
|
||||
- **NativeWorkerReplicas**: Number of Windmill native worker replicas
|
||||
- **Enterprise**: Enable Windmill [Enterprise features](https://www.windmill.dev/docs/misc/plans_details#upgrading-to-enterprise-edition) (requires license key)
|
||||
|
||||
## Customization
|
||||
|
||||
To modify the Helm chart configuration or update the template, refer to the official Windmill Helm chart repository:
|
||||
[https://github.com/windmill-labs/windmill-helm-charts](https://github.com/windmill-labs/windmill-helm-charts)
|
||||
|
||||
## Documentation
|
||||
|
||||
For more information about Windmill's Helm chart deployment options, see:
|
||||
[https://www.windmill.dev/docs/advanced/self_host#helm-chart](https://www.windmill.dev/docs/advanced/self_host#helm-chart)
|
||||
|
||||
For detailed information about setting up RDS for Windmill on AWS:
|
||||
[https://www.windmill.dev/docs/advanced/self_host/aws_ecs#create-a-rds-database](https://www.windmill.dev/docs/advanced/self_host/aws_ecs#create-a-rds-database)
|
||||
|
||||
## Deployment
|
||||
|
||||
1. Upload the CloudFormation template to your AWS account
|
||||
2. Fill in the required parameters
|
||||
3. Deploy the stack
|
||||
4. Access Windmill using the URL provided in the Outputs section of the stack
|
||||
|
||||
After deployment, you can access Windmill via the LoadBalancer URL shown in the CloudFormation stack outputs.
|
||||
@@ -0,0 +1,688 @@
|
||||
AWSTemplateFormatVersion: "2010-09-09"
|
||||
Description: Deploy Windmill on EKS with Helm
|
||||
|
||||
Parameters:
|
||||
NodeInstanceType:
|
||||
Type: String
|
||||
Default: t3.medium
|
||||
AllowedValues:
|
||||
- t3.small
|
||||
- t3.medium
|
||||
- t3.large
|
||||
- t3.xlarge
|
||||
- t3.2xlarge
|
||||
- m5.large
|
||||
- m5.xlarge
|
||||
- m5.2xlarge
|
||||
- m5.4xlarge
|
||||
- c5.large
|
||||
- c5.xlarge
|
||||
- c5.2xlarge
|
||||
- r5.large
|
||||
- r5.xlarge
|
||||
- r5.2xlarge
|
||||
Description: EC2 instance type for the EKS worker nodes
|
||||
NodeGroupSize:
|
||||
Type: Number
|
||||
Default: 2
|
||||
RdsInstanceClass:
|
||||
Type: String
|
||||
Default: db.t3.small
|
||||
AllowedValues:
|
||||
- db.t3.micro
|
||||
- db.t3.small
|
||||
- db.t3.medium
|
||||
- db.t3.large
|
||||
- db.t3.xlarge
|
||||
- db.m5.large
|
||||
- db.m5.xlarge
|
||||
- db.m5.2xlarge
|
||||
- db.r5.large
|
||||
- db.r5.xlarge
|
||||
- db.r5.2xlarge
|
||||
Description: RDS instance class for the PostgreSQL database
|
||||
DBPassword:
|
||||
Type: String
|
||||
NoEcho: true
|
||||
WorkerReplicas:
|
||||
Type: Number
|
||||
Default: 2
|
||||
NativeWorkerReplicas:
|
||||
Type: Number
|
||||
Default: 1
|
||||
Enterprise:
|
||||
Type: String
|
||||
Default: false
|
||||
AllowedValues:
|
||||
- true
|
||||
- false
|
||||
Description: Enable Windmill Enterprise features (requires license key)
|
||||
|
||||
Mappings:
|
||||
RegionMap:
|
||||
us-east-1:
|
||||
AMI: ami-0cff7528ff583bf9a
|
||||
us-east-2:
|
||||
AMI: ami-0cd3c7f72edd5b06d
|
||||
us-west-1:
|
||||
AMI: ami-0d9858aa3c6322f73
|
||||
us-west-2:
|
||||
AMI: ami-098e42ae54c764c35
|
||||
ca-central-1:
|
||||
AMI: ami-00f881f027a6d74a0
|
||||
eu-west-1:
|
||||
AMI: ami-04dd4500af104442f
|
||||
eu-west-2:
|
||||
AMI: ami-0eb260c4d5475b901
|
||||
eu-west-3:
|
||||
AMI: ami-05e8e20cef0eaa9d0
|
||||
eu-central-1:
|
||||
AMI: ami-0bad4a5e987bdebde
|
||||
ap-northeast-1:
|
||||
AMI: ami-0b7546e839d7ace12
|
||||
ap-northeast-2:
|
||||
AMI: ami-0fd0765afb77bcca7
|
||||
ap-southeast-1:
|
||||
AMI: ami-0c802847a7dd848c0
|
||||
ap-southeast-2:
|
||||
AMI: ami-07620139298af599e
|
||||
ap-south-1:
|
||||
AMI: ami-0851b76e8b1bce90b
|
||||
sa-east-1:
|
||||
AMI: ami-054a31f1b3bf90920
|
||||
|
||||
Resources:
|
||||
VPC:
|
||||
Type: AWS::EC2::VPC
|
||||
Properties:
|
||||
CidrBlock: 10.0.0.0/16
|
||||
EnableDnsSupport: true
|
||||
EnableDnsHostnames: true
|
||||
Tags:
|
||||
- Key: Name
|
||||
Value: !Sub "${AWS::StackName}-vpc"
|
||||
- Key: !Sub "kubernetes.io/cluster/${AWS::StackName}-cluster"
|
||||
Value: shared
|
||||
|
||||
InternetGateway:
|
||||
Type: AWS::EC2::InternetGateway
|
||||
|
||||
AttachGateway:
|
||||
Type: AWS::EC2::VPCGatewayAttachment
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
InternetGatewayId: !Ref InternetGateway
|
||||
|
||||
PublicSubnet1:
|
||||
Type: AWS::EC2::Subnet
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
CidrBlock: 10.0.1.0/24
|
||||
AvailabilityZone: !Select [0, !GetAZs ""]
|
||||
MapPublicIpOnLaunch: true
|
||||
Tags:
|
||||
- Key: kubernetes.io/role/elb
|
||||
Value: "1"
|
||||
- Key: !Sub "kubernetes.io/cluster/${AWS::StackName}-cluster"
|
||||
Value: shared
|
||||
- Key: Name
|
||||
Value: !Sub ${AWS::StackName}-public-subnet-1
|
||||
|
||||
PublicSubnet2:
|
||||
Type: AWS::EC2::Subnet
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
CidrBlock: 10.0.2.0/24
|
||||
AvailabilityZone: !Select [1, !GetAZs ""]
|
||||
MapPublicIpOnLaunch: true
|
||||
Tags:
|
||||
- Key: kubernetes.io/role/elb
|
||||
Value: "1"
|
||||
- Key: !Sub "kubernetes.io/cluster/${AWS::StackName}-cluster"
|
||||
Value: shared
|
||||
- Key: Name
|
||||
Value: !Sub ${AWS::StackName}-public-subnet-2
|
||||
|
||||
RouteTable:
|
||||
Type: AWS::EC2::RouteTable
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
|
||||
PublicRoute:
|
||||
Type: AWS::EC2::Route
|
||||
DependsOn: AttachGateway
|
||||
Properties:
|
||||
RouteTableId: !Ref RouteTable
|
||||
DestinationCidrBlock: 0.0.0.0/0
|
||||
GatewayId: !Ref InternetGateway
|
||||
|
||||
SubnetRouteTableAssociation1:
|
||||
Type: AWS::EC2::SubnetRouteTableAssociation
|
||||
DependsOn: PublicRoute
|
||||
Properties:
|
||||
SubnetId: !Ref PublicSubnet1
|
||||
RouteTableId: !Ref RouteTable
|
||||
|
||||
SubnetRouteTableAssociation2:
|
||||
Type: AWS::EC2::SubnetRouteTableAssociation
|
||||
DependsOn: PublicRoute
|
||||
Properties:
|
||||
SubnetId: !Ref PublicSubnet2
|
||||
RouteTableId: !Ref RouteTable
|
||||
|
||||
EKSClusterRole:
|
||||
Type: AWS::IAM::Role
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- eks.amazonaws.com
|
||||
- ec2.amazonaws.com
|
||||
Action:
|
||||
- sts:AssumeRole
|
||||
ManagedPolicyArns:
|
||||
- arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
|
||||
- arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
|
||||
Policies:
|
||||
- PolicyName: EKSAccess
|
||||
PolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- eks:*
|
||||
- ec2:DescribeInstances
|
||||
- ec2:DescribeRouteTables
|
||||
- ec2:DescribeSecurityGroups
|
||||
- ec2:DescribeSubnets
|
||||
- ec2:DescribeVpcs
|
||||
- iam:GetRole
|
||||
- iam:ListRoles
|
||||
Resource: "*"
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- ssm:GetParameter
|
||||
- ssm:PutParameter
|
||||
Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*"
|
||||
- PolicyName: KubernetesAccess
|
||||
PolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- eks:DescribeCluster
|
||||
- eks:ListClusters
|
||||
- eks:AccessKubernetesApi
|
||||
Resource: !Sub "arn:aws:eks:${AWS::Region}:${AWS::AccountId}:cluster/${AWS::StackName}-cluster"
|
||||
|
||||
EKSNodeRole:
|
||||
Type: AWS::IAM::Role
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- ec2.amazonaws.com
|
||||
Action:
|
||||
- sts:AssumeRole
|
||||
ManagedPolicyArns:
|
||||
- arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
|
||||
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
|
||||
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
|
||||
|
||||
EKSCluster:
|
||||
Type: AWS::EKS::Cluster
|
||||
Properties:
|
||||
Name: !Sub "${AWS::StackName}-cluster"
|
||||
RoleArn: !GetAtt EKSClusterRole.Arn
|
||||
ResourcesVpcConfig:
|
||||
SubnetIds:
|
||||
- !Ref PublicSubnet1
|
||||
- !Ref PublicSubnet2
|
||||
EndpointPublicAccess: true
|
||||
AccessConfig:
|
||||
AuthenticationMode: API_AND_CONFIG_MAP
|
||||
BootstrapClusterCreatorAdminPermissions: true
|
||||
DependsOn:
|
||||
- PublicRoute
|
||||
- VPCCleanup
|
||||
|
||||
EKSClusterAccess:
|
||||
Type: AWS::EKS::AccessEntry
|
||||
Properties:
|
||||
ClusterName: !Ref EKSCluster
|
||||
PrincipalArn: !GetAtt EKSClusterRole.Arn
|
||||
Type: STANDARD
|
||||
Username: admin
|
||||
AccessPolicies:
|
||||
- PolicyArn: arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy
|
||||
AccessScope:
|
||||
Type: cluster
|
||||
|
||||
EKSNodeGroup:
|
||||
Type: AWS::EKS::Nodegroup
|
||||
DependsOn:
|
||||
- WindmillDB
|
||||
- EKSCluster
|
||||
Properties:
|
||||
ClusterName: !Ref EKSCluster
|
||||
NodeRole: !GetAtt EKSNodeRole.Arn
|
||||
Subnets:
|
||||
- !Ref PublicSubnet1
|
||||
- !Ref PublicSubnet2
|
||||
ScalingConfig:
|
||||
MinSize: 1
|
||||
DesiredSize: !Ref NodeGroupSize
|
||||
MaxSize: 4
|
||||
InstanceTypes:
|
||||
- !Ref NodeInstanceType
|
||||
|
||||
WindmillDBSubnetGroup:
|
||||
Type: AWS::RDS::DBSubnetGroup
|
||||
Properties:
|
||||
DBSubnetGroupDescription: Subnet group for RDS instance
|
||||
SubnetIds:
|
||||
- !Ref PublicSubnet1
|
||||
- !Ref PublicSubnet2
|
||||
|
||||
WindmillDBSecurityGroup:
|
||||
Type: AWS::EC2::SecurityGroup
|
||||
Properties:
|
||||
GroupDescription: Allow PostgreSQL access from EKS nodes
|
||||
VpcId: !Ref VPC
|
||||
SecurityGroupIngress:
|
||||
- IpProtocol: tcp
|
||||
FromPort: 5432
|
||||
ToPort: 5432
|
||||
CidrIp: 10.0.0.0/16
|
||||
|
||||
WindmillDB:
|
||||
Type: AWS::RDS::DBInstance
|
||||
Properties:
|
||||
DBInstanceIdentifier: !Sub "${AWS::StackName}-db"
|
||||
AllocatedStorage: 20
|
||||
DBInstanceClass: !Ref RdsInstanceClass
|
||||
Engine: postgres
|
||||
EngineVersion: 17.2
|
||||
MasterUsername: postgres
|
||||
MasterUserPassword: !Ref DBPassword
|
||||
DBName: windmill
|
||||
PubliclyAccessible: false
|
||||
DBSubnetGroupName: !Ref WindmillDBSubnetGroup
|
||||
VPCSecurityGroups:
|
||||
- !Ref WindmillDBSecurityGroup
|
||||
DependsOn:
|
||||
- WindmillDBSubnetGroup
|
||||
|
||||
WindmillInstallerInstanceProfile:
|
||||
Type: AWS::IAM::InstanceProfile
|
||||
Properties:
|
||||
Roles:
|
||||
- !Ref EKSClusterRole
|
||||
|
||||
WindmillInstallerSecurityGroup:
|
||||
Type: AWS::EC2::SecurityGroup
|
||||
Properties:
|
||||
GroupDescription: Security group for Windmill installer instance
|
||||
VpcId: !Ref VPC
|
||||
SecurityGroupEgress:
|
||||
- IpProtocol: -1
|
||||
FromPort: -1
|
||||
ToPort: -1
|
||||
CidrIp: 0.0.0.0/0
|
||||
|
||||
WindmillInstaller:
|
||||
Type: AWS::EC2::Instance
|
||||
CreationPolicy:
|
||||
ResourceSignal:
|
||||
Timeout: PT30M # Gives 30 minutes for the installation to complete
|
||||
DependsOn:
|
||||
- EKSNodeGroup
|
||||
- WindmillDB
|
||||
Properties:
|
||||
ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AMI]
|
||||
InstanceType: t3.micro
|
||||
IamInstanceProfile: !Ref WindmillInstallerInstanceProfile
|
||||
SubnetId: !Ref PublicSubnet1
|
||||
SecurityGroupIds:
|
||||
- !Ref WindmillInstallerSecurityGroup
|
||||
UserData:
|
||||
Fn::Base64: !Sub |
|
||||
#!/bin/bash
|
||||
set -e # Exit on any error
|
||||
|
||||
# Install required tools
|
||||
yum update -y
|
||||
yum install -y aws-cli jq postgresql15 aws-cfn-bootstrap
|
||||
|
||||
# Set up logging directory with correct permissions
|
||||
mkdir -p /var/log/windmill-installer
|
||||
touch /var/log/windmill-installer/install.log
|
||||
|
||||
# Create installation directory
|
||||
mkdir -p /opt/windmill-installer
|
||||
|
||||
# Install kubectl
|
||||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
chmod +x kubectl
|
||||
mv kubectl /usr/local/bin/
|
||||
|
||||
# Install helm
|
||||
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
|
||||
chmod +x get_helm.sh
|
||||
./get_helm.sh
|
||||
|
||||
# Create the installation script
|
||||
cat << 'EOF' > /opt/windmill-installer/install.sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Configure kubectl
|
||||
aws sts get-caller-identity > /dev/null
|
||||
export AWS_SDK_LOAD_CONFIG=1
|
||||
export KUBECONFIG=/root/.kube/config
|
||||
aws eks update-kubeconfig --name ${AWS::StackName}-cluster --region ${AWS::Region}
|
||||
|
||||
# Add debugging for each kubectl attempt
|
||||
echo "HOME: $HOME"
|
||||
echo "KUBECONFIG: $KUBECONFIG"
|
||||
echo "User: $(whoami)"
|
||||
echo "AWS Identity: $(aws sts get-caller-identity)"
|
||||
echo "Trying kubectl command..."
|
||||
kubectl get nodes || echo "Command failed with status $?"
|
||||
|
||||
echo "Waiting for EKS nodes to be ready..."
|
||||
while true; do
|
||||
# Force credential refresh on each attempt
|
||||
aws sts get-caller-identity > /dev/null
|
||||
aws eks update-kubeconfig --name ${AWS::StackName}-cluster --region ${AWS::Region}
|
||||
|
||||
if kubectl get nodes &>/dev/null; then
|
||||
READY_NODES=$(kubectl get nodes -o json | jq -r '.items[] | select(.status.conditions[] | select(.type=="Ready" and .status=="True")) | .metadata.name' | wc -l)
|
||||
DESIRED_NODES=${NodeGroupSize}
|
||||
if [ "$READY_NODES" -eq "$DESIRED_NODES" ]; then
|
||||
echo "All nodes are ready"
|
||||
break
|
||||
fi
|
||||
echo "Found $READY_NODES ready nodes out of $DESIRED_NODES desired nodes"
|
||||
else
|
||||
echo "Waiting for cluster access..."
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
|
||||
echo "Installing AWS Load Balancer Controller..."
|
||||
# Install AWS Load Balancer Controller
|
||||
helm repo add eks https://aws.github.io/eks-charts
|
||||
helm repo update
|
||||
|
||||
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
|
||||
-n kube-system \
|
||||
--set clusterName=${AWS::StackName}-cluster \
|
||||
--set region=${AWS::Region} \
|
||||
--set vpcId=${VPC}
|
||||
|
||||
echo "Waiting for AWS Load Balancer Controller to be ready..."
|
||||
kubectl wait --namespace kube-system \
|
||||
--for=condition=ready pod \
|
||||
--selector=app.kubernetes.io/name=aws-load-balancer-controller \
|
||||
--timeout=300s
|
||||
|
||||
echo "Waiting for RDS to be available..."
|
||||
while true; do
|
||||
if pg_isready -h ${WindmillDB.Endpoint.Address} -p 5432 -U postgres 2>/dev/null; then
|
||||
echo "Database is ready"
|
||||
break
|
||||
fi
|
||||
echo "Database not ready yet..."
|
||||
sleep 30
|
||||
done
|
||||
|
||||
echo "Creating namespace and installing Windmill..."
|
||||
kubectl create namespace windmill
|
||||
|
||||
# Add helm repo and install Windmill
|
||||
helm repo add windmill https://windmill-labs.github.io/windmill-helm-charts
|
||||
helm repo update
|
||||
|
||||
helm install ${AWS::StackName} windmill/windmill \
|
||||
--namespace windmill \
|
||||
--set windmill.databaseUrl="postgres://postgres:${DBPassword}@${WindmillDB.Endpoint.Address}/windmill?sslmode=require" \
|
||||
--set windmill.baseDomain=windmill.local \
|
||||
--set windmill.baseProtocol=http \
|
||||
--set windmill.appReplicas=${WorkerReplicas} \
|
||||
--set windmill.lspReplicas=2 \
|
||||
--set windmill.workerGroups[0].name=default \
|
||||
--set windmill.workerGroups[0].mode=worker \
|
||||
--set windmill.workerGroups[0].replicas=${WorkerReplicas} \
|
||||
--set windmill.workerGroups[1].name=native \
|
||||
--set windmill.workerGroups[1].mode=worker \
|
||||
--set windmill.workerGroups[1].replicas=${NativeWorkerReplicas} \
|
||||
--set windmill.app.service.spec.type=LoadBalancer \
|
||||
--set windmill.app.service.spec.sessionAffinity=None \
|
||||
--set windmill.app.service.port=8000 \
|
||||
--set windmill.app.service.ports[0].port=8000 \
|
||||
--set windmill.app.service.ports[0].targetPort=8000 \
|
||||
--set windmill.app.service.ports[0].protocol=TCP \
|
||||
--set postgresql.enabled=false \
|
||||
--set enterprise.enabled=${Enterprise}
|
||||
|
||||
# Change service to LoadBalancer
|
||||
echo "Changing service to LoadBalancer"
|
||||
kubectl patch service windmill-app -n windmill -p '{"spec":{"type":"LoadBalancer","sessionAffinity":"None"}}'
|
||||
kubectl patch service windmill-app -n windmill -p '{"spec":{"ports":[{"name":"api","port":8000,"targetPort":8000,"protocol":"TCP"},{"name":"http","port":80,"targetPort":8000,"protocol":"TCP"}]}}'
|
||||
|
||||
# Wait for LoadBalancer to get an address
|
||||
echo "Waiting for LoadBalancer address..."
|
||||
while true; do
|
||||
LB_HOSTNAME=$(kubectl get svc -n windmill windmill-app -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null)
|
||||
if [ ! -z "$LB_HOSTNAME" ]; then
|
||||
break
|
||||
fi
|
||||
echo "Waiting for LoadBalancer hostname..."
|
||||
sleep 30
|
||||
done
|
||||
|
||||
# Store the LoadBalancer hostname in SSM
|
||||
aws ssm put-parameter \
|
||||
--region ${AWS::Region} \
|
||||
--name "/${AWS::StackName}/loadbalancer-hostname" \
|
||||
--value "$LB_HOSTNAME" \
|
||||
--type "String" \
|
||||
--overwrite
|
||||
|
||||
# Signal CloudFormation that installation is complete
|
||||
echo "Signal CloudFormation that installation is complete"
|
||||
/opt/aws/bin/cfn-signal -e $? \
|
||||
--stack ${AWS::StackName} \
|
||||
--resource WindmillInstaller \
|
||||
--region ${AWS::Region}
|
||||
|
||||
# Self-terminate this instance
|
||||
echo "Self-terminating instance"
|
||||
aws ec2 terminate-instances --instance-ids $(curl -s http://169.254.169.254/latest/meta-data/instance-id) --region ${AWS::Region}
|
||||
EOF
|
||||
|
||||
# Set permissions and run the installation script
|
||||
chmod +x /opt/windmill-installer/install.sh
|
||||
|
||||
# Run the installation script directly (not as ec2-user)
|
||||
cd /opt/windmill-installer && ./install.sh > /var/log/windmill-installer/install.log 2>&1
|
||||
|
||||
LoadBalancerHostnameLookup:
|
||||
Type: Custom::SSMParameterLookup
|
||||
DependsOn: WindmillInstaller
|
||||
Properties:
|
||||
ServiceToken: !GetAtt LookupSSMParameterFunction.Arn
|
||||
ParameterName: !Sub "/${AWS::StackName}/loadbalancer-hostname"
|
||||
|
||||
LookupSSMParameterFunction:
|
||||
Type: AWS::Lambda::Function
|
||||
Properties:
|
||||
Handler: index.handler
|
||||
Role: !GetAtt LambdaExecutionRole.Arn
|
||||
Timeout: 300
|
||||
Runtime: nodejs18.x
|
||||
Code:
|
||||
ZipFile: !Sub |
|
||||
const { SSMClient, GetParameterCommand } = require('@aws-sdk/client-ssm');
|
||||
const response = require('cfn-response');
|
||||
|
||||
exports.handler = async (event, context) => {
|
||||
if (event.RequestType === 'Delete') {
|
||||
return response.send(event, context, response.SUCCESS);
|
||||
}
|
||||
|
||||
try {
|
||||
const ssmClient = new SSMClient();
|
||||
|
||||
// Loop to check for the parameter until it's not "pending"
|
||||
let tries = 0;
|
||||
let paramValue = "pending";
|
||||
|
||||
while (paramValue === "pending" && tries < 20) {
|
||||
const params = {
|
||||
Name: event.ResourceProperties.ParameterName,
|
||||
WithDecryption: false
|
||||
};
|
||||
|
||||
const result = await ssmClient.send(new GetParameterCommand(params));
|
||||
paramValue = result.Parameter.Value;
|
||||
|
||||
if (paramValue === "pending") {
|
||||
await new Promise(resolve => setTimeout(resolve, 15000)); // wait 15 seconds
|
||||
tries++;
|
||||
}
|
||||
}
|
||||
|
||||
return response.send(event, context, response.SUCCESS, {
|
||||
HostnameValue: paramValue
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.send(event, context, response.FAILED, { error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
LambdaExecutionRole:
|
||||
Type: AWS::IAM::Role
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service: lambda.amazonaws.com
|
||||
Action: sts:AssumeRole
|
||||
ManagedPolicyArns:
|
||||
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
|
||||
Policies:
|
||||
- PolicyName: SSMParameterAccess
|
||||
PolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- ssm:GetParameter
|
||||
Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*"
|
||||
|
||||
VPCCleanupFunction:
|
||||
Type: AWS::Lambda::Function
|
||||
Properties:
|
||||
Handler: index.handler
|
||||
Role: !GetAtt VPCCleanupRole.Arn
|
||||
Timeout: 300
|
||||
Runtime: nodejs18.x
|
||||
Code:
|
||||
ZipFile: |
|
||||
const { ElasticLoadBalancingClient, DescribeLoadBalancersCommand,
|
||||
DeleteLoadBalancerCommand } = require('@aws-sdk/client-elastic-load-balancing');
|
||||
const response = require('cfn-response');
|
||||
|
||||
exports.handler = async (event, context) => {
|
||||
if (event.RequestType !== 'Delete') {
|
||||
return response.send(event, context, response.SUCCESS);
|
||||
}
|
||||
|
||||
try {
|
||||
const elb = new ElasticLoadBalancingClient();
|
||||
const vpcId = event.ResourceProperties.VpcId;
|
||||
|
||||
// Find and delete Classic Load Balancers in the VPC
|
||||
const lbResponse = await elb.send(new DescribeLoadBalancersCommand({}));
|
||||
let deleted = false;
|
||||
|
||||
for (const lb of lbResponse.LoadBalancerDescriptions || []) {
|
||||
if (lb.VPCId === vpcId) {
|
||||
console.log(`Deleting Classic Load Balancer: ${lb.LoadBalancerName}`);
|
||||
await elb.send(new DeleteLoadBalancerCommand({
|
||||
LoadBalancerName: lb.LoadBalancerName
|
||||
}));
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
// Wait for deletion to complete
|
||||
console.log('Waiting 30 seconds for load balancer deletion to complete...');
|
||||
await new Promise(r => setTimeout(r, 30000));
|
||||
}
|
||||
|
||||
return response.send(event, context, response.SUCCESS);
|
||||
} catch (error) {
|
||||
console.error('Error deleting load balancers:', error);
|
||||
return response.send(event, context, response.FAILED, {error: error.message});
|
||||
}
|
||||
};
|
||||
|
||||
VPCCleanupRole:
|
||||
Type: AWS::IAM::Role
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service: lambda.amazonaws.com
|
||||
Action: sts:AssumeRole
|
||||
ManagedPolicyArns:
|
||||
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
|
||||
Policies:
|
||||
- PolicyName: VPCCleanupPolicy
|
||||
PolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- ec2:DescribeAddresses
|
||||
- ec2:DisassociateAddress
|
||||
- ec2:DescribeNetworkInterfaces
|
||||
- elasticloadbalancing:DescribeLoadBalancers
|
||||
- elasticloadbalancing:DeleteLoadBalancer
|
||||
- elasticloadbalancingv2:DescribeLoadBalancers
|
||||
- elasticloadbalancingv2:DeleteLoadBalancer
|
||||
Resource: "*"
|
||||
|
||||
VPCCleanup:
|
||||
Type: Custom::VPCCleanup
|
||||
Properties:
|
||||
ServiceToken: !GetAtt VPCCleanupFunction.Arn
|
||||
VpcId: !Ref VPC
|
||||
|
||||
Outputs:
|
||||
ClusterName:
|
||||
Description: EKS cluster name
|
||||
Value: !Sub "${AWS::StackName}-cluster"
|
||||
|
||||
DatabaseEndpoint:
|
||||
Description: RDS instance endpoint
|
||||
Value: !GetAtt WindmillDB.Endpoint.Address
|
||||
|
||||
LoadBalancerHostname:
|
||||
Description: Windmill LoadBalancer hostname
|
||||
Value: !Sub "http://${LoadBalancerHostnameLookup.HostnameValue}"
|
||||
+3
-1
@@ -9,4 +9,6 @@ tests-out/
|
||||
storageState.json
|
||||
.env.production
|
||||
dist/
|
||||
static/tsdocs/
|
||||
static/tsdocs/
|
||||
static/ui_builder/
|
||||
ui_builder.tar.gz
|
||||
Generated
+217
-82
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.481.0",
|
||||
"version": "1.483.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.481.0",
|
||||
"version": "1.483.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^4.0.0",
|
||||
@@ -74,7 +75,7 @@
|
||||
"windmill-parser-wasm-py": "^1.477.1",
|
||||
"windmill-parser-wasm-regex": "^1.481.0",
|
||||
"windmill-parser-wasm-rust": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.438.2",
|
||||
"windmill-parser-wasm-yaml": "^1.429.0",
|
||||
"windmill-sql-datatype-parser-wasm": "^1.318.0",
|
||||
"y-monaco": "^0.1.4",
|
||||
@@ -131,10 +132,10 @@
|
||||
"svelte-splitpanes": "^8.0.9",
|
||||
"svelte2tsx": "^0.6.16",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tar": "^7.4.3",
|
||||
"tslib": "^2.6.1",
|
||||
"typescript": "^5.5.0",
|
||||
"vite": "^6.2.1",
|
||||
"vite-plugin-circular-dependency": "^0.2.1",
|
||||
"vite": "^6.3.2",
|
||||
"vite-plugin-mkcert": "^1.17.5",
|
||||
"yootils": "^0.3.1"
|
||||
},
|
||||
@@ -989,6 +990,27 @@
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minipass": "^7.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass/node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.8",
|
||||
"license": "MIT",
|
||||
@@ -1208,43 +1230,6 @@
|
||||
"version": "0.0.1",
|
||||
"license": "SEE LICENSE IN LICENSE"
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.4",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.35.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz",
|
||||
@@ -2925,11 +2910,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
|
||||
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/citty": {
|
||||
@@ -4434,8 +4420,9 @@
|
||||
},
|
||||
"node_modules/fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0"
|
||||
},
|
||||
@@ -4445,8 +4432,9 @@
|
||||
},
|
||||
"node_modules/fs-minipass/node_modules/minipass": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@@ -4554,11 +4542,74 @@
|
||||
"giget": "dist/cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/giget/node_modules/chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/giget/node_modules/minizlib": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/giget/node_modules/minizlib/node_modules/minipass": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/giget/node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/giget/node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/giget/node_modules/tar": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^5.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"dev": true,
|
||||
@@ -6542,37 +6593,39 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib": {
|
||||
"version": "2.1.2",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
|
||||
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib/node_modules/minipass": {
|
||||
"version": "3.3.6",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
|
||||
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/mlly": {
|
||||
@@ -10153,19 +10206,38 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "6.2.1",
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
|
||||
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^5.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
"@isaacs/fs-minipass": "^4.0.0",
|
||||
"chownr": "^3.0.0",
|
||||
"minipass": "^7.1.2",
|
||||
"minizlib": "^3.0.1",
|
||||
"mkdirp": "^3.0.1",
|
||||
"yallist": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/yallist": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
|
||||
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
@@ -10197,6 +10269,48 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.3",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"dev": true,
|
||||
@@ -10505,13 +10619,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.2.1",
|
||||
"version": "6.3.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz",
|
||||
"integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.3",
|
||||
"picomatch": "^4.0.2",
|
||||
"postcss": "^8.5.3",
|
||||
"rollup": "^4.30.1"
|
||||
"rollup": "^4.34.9",
|
||||
"tinyglobby": "^0.2.12"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -10574,15 +10692,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-circular-dependency": {
|
||||
"version": "0.2.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.2",
|
||||
"chalk": "^4.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-mkcert": {
|
||||
"version": "1.17.7",
|
||||
"dev": true,
|
||||
@@ -10599,6 +10708,32 @@
|
||||
"vite": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/vitefu": {
|
||||
"version": "1.0.6",
|
||||
"dev": true,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.481.0",
|
||||
"version": "1.483.2",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"postinstall": "node scripts/untar_ui_builder.js",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --threshold warning",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||
@@ -64,10 +65,10 @@
|
||||
"svelte-splitpanes": "^8.0.9",
|
||||
"svelte2tsx": "^0.6.16",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tar": "^7.4.3",
|
||||
"tslib": "^2.6.1",
|
||||
"typescript": "^5.5.0",
|
||||
"vite": "^6.2.1",
|
||||
"vite-plugin-circular-dependency": "^0.2.1",
|
||||
"vite": "^6.3.2",
|
||||
"vite-plugin-mkcert": "^1.17.5",
|
||||
"yootils": "^0.3.1"
|
||||
},
|
||||
@@ -143,7 +144,7 @@
|
||||
"windmill-parser-wasm-py": "^1.477.1",
|
||||
"windmill-parser-wasm-regex": "^1.481.0",
|
||||
"windmill-parser-wasm-rust": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.438.2",
|
||||
"windmill-parser-wasm-yaml": "^1.429.0",
|
||||
"windmill-sql-datatype-parser-wasm": "^1.318.0",
|
||||
"y-monaco": "^0.1.4",
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
import { x } from 'tar'
|
||||
|
||||
const tarUrl = 'https://pub-06154ed168a24e73a86ab84db6bf15d8.r2.dev/ui_builder-d44b577.tar.gz'
|
||||
const outputTarPath = path.join(process.cwd(), 'ui_builder.tar.gz')
|
||||
const extractTo = path.join(process.cwd(), 'static/ui_builder/')
|
||||
|
||||
import { fileURLToPath } from 'url'
|
||||
import { dirname } from 'path'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
// Download the tar file
|
||||
const response = await fetch(tarUrl)
|
||||
const buffer = await response.arrayBuffer()
|
||||
await fs.promises.writeFile(outputTarPath, Buffer.from(buffer))
|
||||
|
||||
// Check if this script is being run from the package root
|
||||
const isRootInstall = process.cwd() + '/scripts' === __dirname
|
||||
|
||||
if (isRootInstall) {
|
||||
console.log('Running postinstall: direct install')
|
||||
// Your postinstall logic here
|
||||
} else {
|
||||
console.log('Skipping postinstall: installed as dependency')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Create extract directory if it doesn't exist
|
||||
try {
|
||||
await fs.promises.mkdir(extractTo, { recursive: true })
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
await x({
|
||||
file: outputTarPath,
|
||||
cwd: extractTo,
|
||||
sync: false,
|
||||
gzip: true
|
||||
})
|
||||
|
||||
await fs.promises.unlink(outputTarPath)
|
||||
@@ -38,6 +38,30 @@ export interface ATABootstrapConfig {
|
||||
|
||||
type ModuleMeta = { state: 'loading' }
|
||||
|
||||
export type DepsToGet = {
|
||||
raw: string
|
||||
module: string
|
||||
version: string | undefined
|
||||
}[]
|
||||
|
||||
function getVersionFromRaw(d: string) {
|
||||
if (d.lastIndexOf('@') > 0) {
|
||||
const splitted = d.split('@')
|
||||
let version = splitted.pop()
|
||||
if (version?.startsWith('^') || version?.startsWith('~')) {
|
||||
version = version.slice(1)
|
||||
}
|
||||
return version
|
||||
}
|
||||
return 'latest'
|
||||
}
|
||||
|
||||
export function versionRangeToVersion(version: string) {
|
||||
if (version.startsWith('^') || version.startsWith('~')) {
|
||||
return version.slice(1)
|
||||
}
|
||||
return version
|
||||
}
|
||||
/**
|
||||
* The function which starts up type acquisition,
|
||||
* returns a function which you then pass the initial
|
||||
@@ -57,11 +81,11 @@ export const setupTypeAcquisition = (config: ATABootstrapConfig) => {
|
||||
|
||||
let resLimit = { usage: 0 }
|
||||
|
||||
return async (initialSourceFile: string) => {
|
||||
return async (initialSourceFile: string | DepsToGet) => {
|
||||
estimatedToDownload = 0
|
||||
estimatedDownloaded = 0
|
||||
|
||||
let todo: string[] = [initialSourceFile]
|
||||
let todo: (string | DepsToGet)[] = [initialSourceFile]
|
||||
let next: string[] = []
|
||||
let i = 0
|
||||
let nb = 0
|
||||
@@ -84,34 +108,25 @@ export const setupTypeAcquisition = (config: ATABootstrapConfig) => {
|
||||
}
|
||||
}
|
||||
|
||||
function getVersion(d: string) {
|
||||
if (d.lastIndexOf('@') > 0) {
|
||||
const splitted = d.split('@')
|
||||
let version = splitted.pop()
|
||||
if (version?.startsWith('^') || version?.startsWith('~')) {
|
||||
version = version.slice(1)
|
||||
}
|
||||
return version
|
||||
}
|
||||
return 'latest'
|
||||
}
|
||||
|
||||
async function resolveDeps(
|
||||
initialSourceFile: string,
|
||||
depsSource: string | DepsToGet,
|
||||
depth: number,
|
||||
resLimit: ResLimit
|
||||
): Promise<string[]> {
|
||||
let depsToGet = config
|
||||
.depsParser(initialSourceFile)
|
||||
.map((d: string) => {
|
||||
let raw = mapModuleNameToModule(d)
|
||||
return {
|
||||
raw,
|
||||
module: raw.lastIndexOf('@') > 0 ? raw.split('@').slice(0, -1).join('@') : raw,
|
||||
version: getVersion(d)
|
||||
}
|
||||
})
|
||||
.filter((f) => !moduleMap.has(f.raw))
|
||||
let depsToGet =
|
||||
typeof depsSource == 'object'
|
||||
? depsSource
|
||||
: config
|
||||
.depsParser(depsSource)
|
||||
.map((d: string) => {
|
||||
let raw = mapModuleNameToModule(d)
|
||||
return {
|
||||
raw,
|
||||
module: raw.lastIndexOf('@') > 0 ? raw.split('@').slice(0, -1).join('@') : raw,
|
||||
version: getVersionFromRaw(raw)
|
||||
}
|
||||
})
|
||||
.filter((f) => !moduleMap.has(f.raw))
|
||||
|
||||
if (depth == 0) {
|
||||
const relativeDeps = depsToGet.filter((f) => isTypescriptRelativePath(f.raw))
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
type ResourceType,
|
||||
type GetGlobalConnectedRepositoriesResponse
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore, enterpriseLicense, userWorkspaces, userStore } from '$lib/stores'
|
||||
import {
|
||||
workspaceStore,
|
||||
enterpriseLicense,
|
||||
userWorkspaces,
|
||||
userStore,
|
||||
workspaceColor
|
||||
} from '$lib/stores'
|
||||
import { base } from '$lib/base'
|
||||
import { emptySchema, emptyString } from '$lib/utils'
|
||||
import SchemaForm from './SchemaForm.svelte'
|
||||
@@ -147,9 +153,7 @@
|
||||
viewJsonSchema = false
|
||||
try {
|
||||
schema = resourceTypeInfo.schema as any
|
||||
|
||||
schema.order = schema.order ?? Object.keys(schema.properties).sort()
|
||||
|
||||
notFound = false
|
||||
} catch (e) {
|
||||
notFound = true
|
||||
@@ -516,14 +520,10 @@
|
||||
<tr class="border-t border-gray-200 dark:border-gray-700">
|
||||
<td class="py-2">{installation.account_id}</td>
|
||||
<td class="py-2">
|
||||
{#if $userWorkspaces.find((w) => w.id === installation.workspace_id)?.color}
|
||||
{#if $workspaceColor}
|
||||
<span
|
||||
class="inline-flex items-center px-2 py-0.5 rounded text-xs"
|
||||
style="background-color: {$userWorkspaces.find(
|
||||
(w) => w.id === installation.workspace_id
|
||||
)?.color}20; color: {$userWorkspaces.find(
|
||||
(w) => w.id === installation.workspace_id
|
||||
)?.color}"
|
||||
style="background-color: {$workspaceColor}20; color: {$workspaceColor}"
|
||||
>
|
||||
{installation.workspace_id}
|
||||
</span>
|
||||
|
||||
@@ -375,6 +375,9 @@
|
||||
(e.ctrlKey || e.metaKey) &&
|
||||
(e.key == 'Enter' || e.key == 'c' || e.key == 'v' || e.key == 'x')
|
||||
) {
|
||||
if (e.key == 'Enter') {
|
||||
dispatch('keydownCmdEnter')
|
||||
}
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
@@ -846,7 +849,7 @@
|
||||
}}
|
||||
bind:args={value}
|
||||
dndType={`nested-${title}`}
|
||||
schemaSkippedValues={['label']}
|
||||
hiddenArgs={['label']}
|
||||
on:reorder={(e) => {
|
||||
if (oneOf && oneOf[objIdx]) {
|
||||
const keys = e.detail
|
||||
@@ -865,7 +868,7 @@
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
schemaSkippedValues={['label']}
|
||||
hiddenArgs={['label']}
|
||||
schema={{
|
||||
properties: obj.properties,
|
||||
order: obj.order,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { BROWSER } from 'esm-env'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
import '@codingame/monaco-vscode-standalone-languages'
|
||||
// import '@codingame/monaco-vscode-standalone-languages'
|
||||
import '@codingame/monaco-vscode-standalone-json-language-features'
|
||||
import '@codingame/monaco-vscode-standalone-typescript-language-features'
|
||||
import { editor as meditor } from 'monaco-editor'
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
|
||||
|
||||
export let schema: Schema | any
|
||||
export let schemaSkippedValues: string[] = []
|
||||
export let hiddenArgs: string[] = []
|
||||
export let args: Record<string, any> = {}
|
||||
export let shouldHideNoInputs: boolean = false
|
||||
export let noVariablePicker = false
|
||||
@@ -439,7 +439,7 @@
|
||||
</div>
|
||||
{#if opened === argName}
|
||||
<div class="p-4 border-t">
|
||||
{#if !schemaSkippedValues.includes(argName) && Object.keys(schema?.properties ?? {}).includes(argName)}
|
||||
{#if !hiddenArgs.includes(argName) && Object.keys(schema?.properties ?? {}).includes(argName)}
|
||||
{#if typeof args == 'object' && schema?.properties[argName]}
|
||||
<PropertyEditor
|
||||
bind:description={schema.properties[argName].description}
|
||||
|
||||
@@ -7,22 +7,6 @@
|
||||
|
||||
languages.typescript.typescriptDefaults.addExtraLib(processStdContent, 'process.d.ts')
|
||||
|
||||
// languages.typescript.javascriptDefaults.setModeConfiguration({
|
||||
// completionItems: true,
|
||||
// hovers: true,
|
||||
// documentSymbols: true,
|
||||
// definitions: true,
|
||||
// references: true,
|
||||
// documentHighlights: true,
|
||||
// rename: true,
|
||||
// diagnostics: true,
|
||||
// documentRangeFormattingEdits: true,
|
||||
// signatureHelp: true,
|
||||
// onTypeFormattingEdits: true,
|
||||
// codeActions: true,
|
||||
// inlayHints: true
|
||||
// })
|
||||
|
||||
languages.typescript.typescriptDefaults.setModeConfiguration({
|
||||
completionItems: true,
|
||||
hovers: true,
|
||||
@@ -52,8 +36,9 @@
|
||||
languages.typescript.typescriptDefaults.setDiagnosticsOptions({
|
||||
noSemanticValidation: false,
|
||||
noSyntaxValidation: false,
|
||||
|
||||
noSuggestionDiagnostics: false,
|
||||
diagnosticCodesToIgnore: [1108]
|
||||
diagnosticCodesToIgnore: [1108, 7006, 7034, 7019, 7005]
|
||||
})
|
||||
|
||||
languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
@@ -80,23 +65,10 @@
|
||||
strict: true,
|
||||
noLib: false,
|
||||
allowImportingTsExtensions: true,
|
||||
moduleResolution: languages.typescript.ModuleResolutionKind.NodeJs
|
||||
allowSyntheticDefaultImports: true,
|
||||
moduleResolution: languages.typescript.ModuleResolutionKind.NodeJs,
|
||||
jsx: languages.typescript.JsxEmit.React
|
||||
})
|
||||
|
||||
// languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||
// target: languages.typescript.ScriptTarget.Latest,
|
||||
// allowNonTsExtensions: true,
|
||||
// noSemanticValidation: false,
|
||||
// noSyntaxValidation: false,
|
||||
// allowImportingTsExtensions: true,
|
||||
// checkJs: true,
|
||||
// allowJs: true,
|
||||
// noUnusedParameters: true,
|
||||
// noUnusedLocals: true,
|
||||
// strict: true,
|
||||
// noLib: true,
|
||||
// moduleResolution: languages.typescript.ModuleResolutionKind.NodeJs
|
||||
// })
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -120,6 +92,8 @@
|
||||
|
||||
import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'
|
||||
import { CloseAction, ErrorAction, RequestType } from 'vscode-languageclient'
|
||||
import type { DocumentUri, MessageTransports } from 'vscode-languageclient'
|
||||
|
||||
import { MonacoBinding } from 'y-monaco'
|
||||
import {
|
||||
dbSchemas,
|
||||
@@ -135,16 +109,18 @@
|
||||
createHash as randomHash,
|
||||
editorConfig,
|
||||
langToExt,
|
||||
updateOptions
|
||||
updateOptions,
|
||||
extToLang
|
||||
} from '$lib/editorUtils'
|
||||
import type { Disposable } from 'vscode'
|
||||
import type { DocumentUri, MessageTransports } from 'vscode-languageclient'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { type Preview, ResourceService, UserService } from '$lib/gen'
|
||||
import type { Text } from 'yjs'
|
||||
import { initializeVscode } from '$lib/components/vscode'
|
||||
|
||||
import { initializeMode } from 'monaco-graphql/esm/initializeMode.js'
|
||||
import type { MonacoGraphQLAPI } from 'monaco-graphql/esm/api.js'
|
||||
|
||||
import {
|
||||
editor as meditor,
|
||||
languages,
|
||||
@@ -154,7 +130,6 @@
|
||||
type IRange,
|
||||
type IDisposable
|
||||
} from 'monaco-editor'
|
||||
import type { MonacoGraphQLAPI } from 'monaco-graphql/esm/api.js'
|
||||
|
||||
import EditorTheme from './EditorTheme.svelte'
|
||||
import {
|
||||
@@ -165,37 +140,27 @@
|
||||
POSTGRES_TYPES,
|
||||
SNOWFLAKE_TYPES
|
||||
} from '$lib/consts'
|
||||
import { setupTypeAcquisition } from '$lib/ata/index'
|
||||
import { setupTypeAcquisition, type DepsToGet } from '$lib/ata/index'
|
||||
import { initWasmTs } from '$lib/infer'
|
||||
import { initVim } from './monaco_keybindings'
|
||||
import { buildWorkerDefinition } from '$lib/monaco_workers/build_workers'
|
||||
import { parseTypescriptDeps } from '$lib/relative_imports'
|
||||
|
||||
import { scriptLangToEditorLang } from '$lib/scripts'
|
||||
import * as htmllang from '$lib/svelteMonarch'
|
||||
import { conf, language } from '$lib/vueMonarch'
|
||||
|
||||
import { Autocompletor } from './copilot/autocomplete/monaco-adapter'
|
||||
import { AIChatEditorHandler } from './copilot/chat/monaco-adapter'
|
||||
import GlobalReviewButtons from './copilot/chat/GlobalReviewButtons.svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { formatResourceTypes } from './copilot/chat/core'
|
||||
import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte'
|
||||
// import EditorTheme from './EditorTheme.svelte'
|
||||
|
||||
let divEl: HTMLDivElement | null = null
|
||||
let editor: meditor.IStandaloneCodeEditor | null = null
|
||||
|
||||
export let lang:
|
||||
| 'typescript'
|
||||
| 'python'
|
||||
| 'go'
|
||||
| 'shell'
|
||||
| 'sql'
|
||||
| 'graphql'
|
||||
| 'powershell'
|
||||
| 'php'
|
||||
| 'css'
|
||||
| 'javascript'
|
||||
| 'rust'
|
||||
| 'yaml'
|
||||
| 'csharp'
|
||||
| 'nu'
|
||||
| 'java'
|
||||
// for related places search: ADD_NEW_LANG
|
||||
export let code: string = ''
|
||||
export let cmdEnterAction: (() => void) | undefined = undefined
|
||||
@@ -217,22 +182,21 @@
|
||||
export let args: Record<string, any> | undefined = undefined
|
||||
export let useWebsockets: boolean = true
|
||||
export let small = false
|
||||
export let scriptLang: Preview['language'] | 'bunnative'
|
||||
export let scriptLang: Preview['language'] | 'bunnative' | 'tsx' | 'jsx' | 'json' | undefined
|
||||
export let disabled: boolean = false
|
||||
export let lineNumbersMinChars = 3
|
||||
export let files: Record<string, { code: string; readonly?: boolean }> | undefined = {}
|
||||
export let extraLib: string | undefined = undefined
|
||||
export let changeTimeout: number = 500
|
||||
export let isAiPanelOpen: boolean = false
|
||||
export let loadAsync = false
|
||||
|
||||
const rHash = randomHash()
|
||||
let lang = scriptLangToEditorLang(scriptLang)
|
||||
$: lang = scriptLangToEditorLang(scriptLang)
|
||||
|
||||
let filePath = computePath(path)
|
||||
$: filePath = computePath(path)
|
||||
|
||||
function computePath(path: string | undefined): string {
|
||||
if (path == '' || path == undefined || path.startsWith('/')) {
|
||||
return rHash
|
||||
} else {
|
||||
return path as string
|
||||
}
|
||||
}
|
||||
|
||||
let initialPath: string | undefined = path
|
||||
|
||||
$: path != initialPath &&
|
||||
@@ -251,15 +215,59 @@
|
||||
let dbSchema: DBSchema | undefined = undefined
|
||||
|
||||
let destroyed = false
|
||||
const uri =
|
||||
lang != 'go' && lang != 'typescript' && lang != 'python' && lang != 'nu'
|
||||
? `file:///${filePath ?? rHash}.${langToExt(lang)}`
|
||||
: `file:///tmp/monaco/${randomHash()}.${langToExt(lang)}`
|
||||
const uri = computeUri(filePath, scriptLang)
|
||||
|
||||
console.log('uri', uri)
|
||||
|
||||
buildWorkerDefinition()
|
||||
|
||||
function computeUri(filePath: string, scriptLang: string | undefined) {
|
||||
let file
|
||||
if (filePath.includes('.')) {
|
||||
file = filePath
|
||||
} else {
|
||||
file = `${filePath}.${scriptLang == 'tsx' ? 'tsx' : langToExt(lang)}`
|
||||
}
|
||||
if (file.startsWith('/')) {
|
||||
file = file.slice(1)
|
||||
}
|
||||
return !['deno', 'go', 'python3'].includes(scriptLang ?? '')
|
||||
? `file:///${file}`
|
||||
: `file:///tmp/monaco/${file}`
|
||||
}
|
||||
|
||||
function computePath(path: string | undefined): string {
|
||||
if (
|
||||
['deno', 'go', 'python3'].includes(scriptLang ?? '') ||
|
||||
path == '' ||
|
||||
path == undefined //||path.startsWith('/')
|
||||
) {
|
||||
return randomHash()
|
||||
} else {
|
||||
console.log('path', path)
|
||||
return path as string
|
||||
}
|
||||
}
|
||||
|
||||
export function switchToFile(path: string, value: string, lang: string) {
|
||||
if (editor) {
|
||||
const uri = mUri.parse(path)
|
||||
console.log('switching to file', path, lang)
|
||||
// vscode.workspace.fs.writeFile(uri, new TextEncoder().encode(value))
|
||||
let nmodel = meditor.getModel(uri)
|
||||
if (nmodel) {
|
||||
console.log('using existing model', path)
|
||||
editor.setModel(nmodel)
|
||||
} else {
|
||||
console.log('creating model', path)
|
||||
nmodel = meditor.createModel(value, lang, uri)
|
||||
editor.setModel(nmodel)
|
||||
}
|
||||
model = nmodel
|
||||
setTypescriptExtraLibs()
|
||||
}
|
||||
}
|
||||
|
||||
export function getCode(): string {
|
||||
return editor?.getValue() ?? ''
|
||||
}
|
||||
@@ -716,9 +724,21 @@
|
||||
}
|
||||
|
||||
export async function reloadWebsocket() {
|
||||
console.log('reloadWebsocket')
|
||||
await closeWebsockets()
|
||||
|
||||
if (
|
||||
!useWebsockets ||
|
||||
!(
|
||||
(lang == 'typescript' && scriptLang === 'deno') ||
|
||||
lang == 'python' ||
|
||||
lang == 'go' ||
|
||||
lang == 'shell'
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
console.log('reloadWebsocket')
|
||||
|
||||
function createLanguageClient(
|
||||
transports: MessageTransports,
|
||||
name: string,
|
||||
@@ -733,6 +753,7 @@
|
||||
documentSelector: [lang],
|
||||
errorHandler: {
|
||||
error: () => ({ action: ErrorAction.Continue }),
|
||||
|
||||
closed: () => ({
|
||||
action: CloseAction.Restart
|
||||
})
|
||||
@@ -864,11 +885,6 @@
|
||||
const hostname = getHostname()
|
||||
|
||||
let encodedImportMap = ''
|
||||
// if (lang == 'typescript') {
|
||||
|
||||
// let worker = await languages.typescript.getTypeScriptWorker()
|
||||
// console.log(worker)
|
||||
// }
|
||||
|
||||
if (useWebsockets) {
|
||||
if (lang == 'typescript' && scriptLang === 'deno') {
|
||||
@@ -1033,7 +1049,8 @@
|
||||
!websocketAlive.go &&
|
||||
!websocketAlive.shellcheck &&
|
||||
!websocketAlive.ruff &&
|
||||
scriptLang != 'bun'
|
||||
scriptLang != 'bun' &&
|
||||
scriptLang != 'tsx'
|
||||
) {
|
||||
console.log('reconnecting to language servers')
|
||||
lastWsAttempt = new Date()
|
||||
@@ -1108,7 +1125,7 @@
|
||||
}
|
||||
|
||||
let initialized = false
|
||||
let ata: ((s: string) => void) | undefined = undefined
|
||||
let ata: ((s: string | DepsToGet) => void) | undefined = undefined
|
||||
|
||||
let statusDiv: Element | null = null
|
||||
|
||||
@@ -1130,16 +1147,93 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: files && model && onFileChanges()
|
||||
|
||||
let svelteRegistered = false
|
||||
let vueRegistered = false
|
||||
function onFileChanges() {
|
||||
if (files && Object.keys(files).find((x) => x.endsWith('.svelte')) != undefined) {
|
||||
if (!svelteRegistered) {
|
||||
svelteRegistered = true
|
||||
languages.register({
|
||||
id: 'svelte',
|
||||
extensions: ['.svelte'],
|
||||
aliases: ['Svelte', 'svelte'],
|
||||
mimetypes: ['application/svelte']
|
||||
})
|
||||
languages.setLanguageConfiguration('svelte', htmllang.conf as any)
|
||||
|
||||
languages.setMonarchTokensProvider('svelte', htmllang.language as any)
|
||||
}
|
||||
}
|
||||
|
||||
if (files && Object.keys(files).find((x) => x.endsWith('.vue')) != undefined) {
|
||||
if (!vueRegistered) {
|
||||
vueRegistered = true
|
||||
languages.register({
|
||||
id: 'vue',
|
||||
extensions: ['.vue'],
|
||||
aliases: ['Vue', 'Vue'],
|
||||
mimetypes: ['application/svelte']
|
||||
})
|
||||
languages.setLanguageConfiguration('vue', conf as any)
|
||||
|
||||
languages.setMonarchTokensProvider('vue', language as any)
|
||||
}
|
||||
}
|
||||
|
||||
if (files && model) {
|
||||
for (const [path, { code, readonly }] of Object.entries(files)) {
|
||||
const luri = mUri.file(path)
|
||||
if (luri.toString() != model.uri.toString()) {
|
||||
let nmodel = meditor.getModel(luri)
|
||||
|
||||
if (nmodel == undefined) {
|
||||
const lmodel = meditor.createModel(code, extToLang(path?.split('.')?.pop()!), luri)
|
||||
if (readonly) {
|
||||
lmodel.onDidChangeContent((evt) => {
|
||||
// This will effectively undo any new edits
|
||||
if (lmodel.getValue() != code && code) {
|
||||
lmodel.setValue(code)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const lmodel = meditor.getModel(luri)
|
||||
if (lmodel && code) {
|
||||
lmodel.setValue(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let timeoutModel: NodeJS.Timeout | undefined = undefined
|
||||
async function loadMonaco() {
|
||||
console.log('path', uri)
|
||||
|
||||
try {
|
||||
console.log("Loading Monaco's language client")
|
||||
await initializeVscode('editor')
|
||||
await initializeVscode('editor', divEl!)
|
||||
console.log('done loading Monaco and vscode')
|
||||
} catch (e) {
|
||||
console.log('error initializing services', e)
|
||||
}
|
||||
|
||||
// vscode.languages.registerDefinitionProvider('*', {
|
||||
// provideDefinition(document, position, token) {
|
||||
// // Get the word under the cursor (this will be the import or function being clicked)
|
||||
// const wordRange = document.getWordRangeAtPosition(position)
|
||||
// const word = document.getText(wordRange)
|
||||
|
||||
// // Do something with the word (for example, log it or handle it)
|
||||
// console.log('Clicked on import or symbol:', word)
|
||||
|
||||
// // Optionally, you can also return a definition location
|
||||
// return null // If you don't want to override the default behavior
|
||||
// }
|
||||
// })
|
||||
// console.log('bef ready')
|
||||
// console.log('af ready')
|
||||
|
||||
@@ -1157,6 +1251,8 @@
|
||||
}
|
||||
model.updateOptions(lang == 'python' ? { tabSize: 4, insertSpaces: true } : updateOptions)
|
||||
|
||||
onFileChanges()
|
||||
|
||||
editor = meditor.create(divEl as HTMLDivElement, {
|
||||
...editorConfig(code, lang, automaticLayout, fixedOverflowWidgets),
|
||||
model,
|
||||
@@ -1176,12 +1272,14 @@
|
||||
timeoutModel = setTimeout(() => {
|
||||
let ncode = getCode()
|
||||
code = ncode
|
||||
dispatch('change', code)
|
||||
}, 500)
|
||||
dispatch('change', ncode)
|
||||
}, changeTimeout)
|
||||
|
||||
ataModel && clearTimeout(ataModel)
|
||||
ataModel = setTimeout(() => {
|
||||
ata?.(getCode())
|
||||
if (scriptLang == 'bun') {
|
||||
ata?.(getCode())
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
@@ -1238,10 +1336,8 @@
|
||||
!websocketAlive.ruff &&
|
||||
!websocketAlive.shellcheck &&
|
||||
!websocketAlive.go &&
|
||||
!websocketInterval &&
|
||||
scriptLang != 'bun'
|
||||
!websocketInterval
|
||||
) {
|
||||
console.log('reconnecting to language servers on focus')
|
||||
reloadWebsocket()
|
||||
}
|
||||
})
|
||||
@@ -1265,6 +1361,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchPackageDeps(deps: DepsToGet) {
|
||||
ata?.(deps)
|
||||
}
|
||||
|
||||
async function setTypescriptRTNamespace() {
|
||||
if (
|
||||
scriptLang &&
|
||||
@@ -1287,73 +1387,78 @@
|
||||
}
|
||||
|
||||
async function setTypescriptExtraLibs() {
|
||||
if (lang === 'typescript' && scriptLang != 'deno') {
|
||||
if (extraLib) {
|
||||
const uri = mUri.parse('file:///extraLib.d.ts')
|
||||
languages.typescript.typescriptDefaults.addExtraLib(extraLib, uri.toString())
|
||||
}
|
||||
if (lang === 'typescript' && (scriptLang == 'bun' || scriptLang == 'tsx') && ata == undefined) {
|
||||
const hostname = getHostname()
|
||||
|
||||
if (scriptLang == 'bun' && ata == undefined) {
|
||||
const addLibraryToRuntime = async (code: string, _path: string) => {
|
||||
const path = 'file://' + _path
|
||||
let uri = mUri.parse(path)
|
||||
console.log('adding library to runtime', path)
|
||||
languages.typescript.typescriptDefaults.addExtraLib(code, path)
|
||||
try {
|
||||
await vscode.workspace.fs.writeFile(uri, new TextEncoder().encode(code))
|
||||
} catch (e) {
|
||||
console.log('error writing file', e)
|
||||
}
|
||||
const addLibraryToRuntime = async (code: string, _path: string) => {
|
||||
const path = 'file://' + _path
|
||||
let uri = mUri.parse(path)
|
||||
console.log('adding library to runtime', path)
|
||||
languages.typescript.typescriptDefaults.addExtraLib(code, path)
|
||||
try {
|
||||
await vscode.workspace.fs.writeFile(uri, new TextEncoder().encode(code))
|
||||
} catch (e) {
|
||||
console.log('error writing file', e)
|
||||
}
|
||||
}
|
||||
|
||||
const addLocalFile = async (code: string, _path: string) => {
|
||||
let p = new URL(_path, uri).href
|
||||
// if (_path?.startsWith('/')) {
|
||||
// p = 'file://' + p
|
||||
// }
|
||||
let nuri = mUri.parse(p)
|
||||
console.log('adding local file', _path, nuri.toString())
|
||||
if (editor) {
|
||||
let localModel = meditor.getModel(nuri)
|
||||
if (localModel) {
|
||||
localModel.setValue(code)
|
||||
} else {
|
||||
meditor.createModel(code, 'typescript', nuri)
|
||||
}
|
||||
try {
|
||||
if (model) {
|
||||
model?.setValue(model.getValue())
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error resetting model', e)
|
||||
const addLocalFile = async (code: string, _path: string) => {
|
||||
let p = new URL(_path, uri).href
|
||||
// if (_path?.startsWith('/')) {
|
||||
// p = 'file://' + p
|
||||
// }
|
||||
let nuri = mUri.parse(p)
|
||||
console.log('adding local file', _path, nuri.toString())
|
||||
if (editor) {
|
||||
let localModel = meditor.getModel(nuri)
|
||||
if (localModel) {
|
||||
localModel.setValue(code)
|
||||
} else {
|
||||
meditor.createModel(code, 'typescript', nuri)
|
||||
}
|
||||
try {
|
||||
if (model) {
|
||||
model?.setValue(model.getValue())
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error resetting model', e)
|
||||
}
|
||||
}
|
||||
await initWasmTs()
|
||||
const root = await genRoot(hostname)
|
||||
console.log('SETUP TYPE ACQUISITION', { root, path })
|
||||
ata = setupTypeAcquisition({
|
||||
projectName: 'Windmill',
|
||||
depsParser: (c) => {
|
||||
return parseTypescriptDeps(c)
|
||||
}
|
||||
await initWasmTs()
|
||||
const root = await genRoot(hostname)
|
||||
console.log('SETUP TYPE ACQUISITION', { root, path })
|
||||
ata = setupTypeAcquisition({
|
||||
projectName: 'Windmill',
|
||||
depsParser: (c) => {
|
||||
return parseTypescriptDeps(c)
|
||||
},
|
||||
root,
|
||||
scriptPath: path,
|
||||
logger: console,
|
||||
delegate: {
|
||||
receivedFile: addLibraryToRuntime,
|
||||
localFile: addLocalFile,
|
||||
progress: (downloaded: number, total: number) => {
|
||||
// console.log({ dl, ttl })
|
||||
},
|
||||
root,
|
||||
scriptPath: path,
|
||||
logger: console,
|
||||
delegate: {
|
||||
receivedFile: addLibraryToRuntime,
|
||||
localFile: addLocalFile,
|
||||
progress: (downloaded: number, total: number) => {
|
||||
// console.log({ dl, ttl })
|
||||
},
|
||||
started: () => {
|
||||
console.log('ATA start')
|
||||
},
|
||||
finished: (f) => {
|
||||
console.log('ATA done')
|
||||
}
|
||||
started: () => {
|
||||
console.log('ATA start')
|
||||
},
|
||||
finished: (f) => {
|
||||
console.log('ATA done')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
if (scriptLang == 'bun') {
|
||||
ata?.('import "bun-types"')
|
||||
ata?.(code)
|
||||
}
|
||||
dispatch('ataReady')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1374,9 +1479,14 @@
|
||||
})
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
if (BROWSER) {
|
||||
loadMonaco().then((x) => (disposeMethod = x))
|
||||
if (loadAsync) {
|
||||
setTimeout(() => loadMonaco().then((x) => (disposeMethod = x)), 0)
|
||||
} else {
|
||||
let m = await loadMonaco()
|
||||
disposeMethod = m
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1408,6 +1518,11 @@
|
||||
</script>
|
||||
|
||||
<EditorTheme />
|
||||
{#if !editor}
|
||||
<div class="inset-0 absolute overflow-clip">
|
||||
<FakeMonacoPlaceHolder {code} />
|
||||
</div>
|
||||
{/if}
|
||||
<div bind:this={divEl} class="{$$props.class} editor {disabled ? 'disabled' : ''}"></div>
|
||||
{#if $vimMode}
|
||||
<div class="fixed bottom-0 z-30" bind:this={statusDiv}></div>
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
<Module.default
|
||||
disabled={!$enterpriseLicense || !isSlackHandler(handlerPath)}
|
||||
schema={slackHandlerSchema}
|
||||
schemaSkippedValues={['slack']}
|
||||
hiddenArgs={['slack']}
|
||||
schemaFieldTooltip={{
|
||||
channel: 'Slack channel name without the "#" - example: "windmill-alerts"'
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<!-- Used to avoid height jitter when loading monaco asynchronously -->
|
||||
|
||||
<script lang="ts">
|
||||
import { getOS } from '$lib/utils'
|
||||
|
||||
type Props = {
|
||||
code?: string
|
||||
autoheight?: boolean
|
||||
lineNumbersWidth?: number
|
||||
lineNumbersOffset?: number
|
||||
class?: string
|
||||
fontSize?: number
|
||||
}
|
||||
|
||||
let {
|
||||
code,
|
||||
autoheight = false,
|
||||
lineNumbersWidth = 51,
|
||||
lineNumbersOffset = 0,
|
||||
class: className = '',
|
||||
fontSize = 14
|
||||
}: Props = $props()
|
||||
|
||||
// https://github.com/microsoft/vscode/blob/baa2dad3cdacd97ac02eff0604984faf1167ff1e/src/vs/editor/common/config/editorOptions.ts#L5421
|
||||
const DEFAULT_WINDOWS_FONT_FAMILY = "Consolas, 'Courier New', monospace"
|
||||
const DEFAULT_MAC_FONT_FAMILY = "Menlo, Monaco, 'Courier New', monospace"
|
||||
const DEFAULT_LINUX_FONT_FAMILY = "'Droid Sans Mono', 'monospace', monospace"
|
||||
const fontFamily =
|
||||
getOS() === 'Windows'
|
||||
? DEFAULT_WINDOWS_FONT_FAMILY
|
||||
: getOS() === 'macOS'
|
||||
? DEFAULT_MAC_FONT_FAMILY
|
||||
: DEFAULT_LINUX_FONT_FAMILY
|
||||
|
||||
// https://github.com/microsoft/vscode/blob/baa2dad3cdacd97ac02eff0604984faf1167ff1e/src/vs/editor/common/config/fontInfo.ts#L14
|
||||
const GOLDEN_LINE_HEIGHT_RATIO = getOS() == 'macOS' ? 1.5 : 1.35
|
||||
|
||||
let lines = $derived(code?.split('\n') ?? [])
|
||||
|
||||
const charWidth = 9 // try to match as closely as possible to monaco editor
|
||||
|
||||
const lineHeight = fontSize * GOLDEN_LINE_HEIGHT_RATIO
|
||||
|
||||
let [clientWidth, clientHeight] = $state([0, 0])
|
||||
let showHorizontalScrollbar = $derived(
|
||||
lines.some((line) => line.length * charWidth > clientWidth - 40)
|
||||
)
|
||||
|
||||
let [editorWidth, editorHeight] = $derived([
|
||||
clientWidth,
|
||||
autoheight ? lines.length * lineHeight + (showHorizontalScrollbar ? 12 : 0) : clientHeight
|
||||
])
|
||||
</script>
|
||||
|
||||
<!-- Copy pasted from actual monaco editor in the web inspector -->
|
||||
|
||||
<div
|
||||
bind:clientWidth
|
||||
bind:clientHeight
|
||||
class="h-full w-full relative editor dark:bg-[#272D38] {className}"
|
||||
style="--vscode-editorCodeLens-lineHeight: 18px; --vscode-editorCodeLens-fontSize: 12px; --vscode-editorCodeLens-fontFeatureSettings: 'liga' off, 'calt' off; --code-editorInlayHintsFontFamily: {fontFamily};"
|
||||
>
|
||||
<div
|
||||
class="monaco-editor no-user-select mac standalone showUnused showDeprecated vs-dark"
|
||||
role="code"
|
||||
>
|
||||
<div class="overflow-guard" style="width: {editorWidth}px; height: {editorHeight}px;">
|
||||
<div
|
||||
class="margin"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
style="position: absolute; transform: translate3d(0px, 0px, 0px); contain: strict; top: 0px; width: {editorWidth}px; height: {editorHeight}px;"
|
||||
>
|
||||
<div
|
||||
class="margin-view-overlays"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
style="position: absolute; font-family: {fontFamily}; font-weight: normal; font-size: {fontSize}px; font-feature-settings: 'liga' 0, 'calt' 0; font-variation-settings: normal; line-height: {lineHeight}px; letter-spacing: 0px; width: {lineNumbersWidth}px; height: 4893px;"
|
||||
>
|
||||
{#each lines as _, i}
|
||||
<div style="top:{lineHeight * i}px;height:{lineHeight}px;">
|
||||
<div class="line-numbers" style="left:{lineNumbersOffset}px;width:25px;">{i + 1}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="monaco-scrollable-element editor-scrollable vs-dark mac"
|
||||
style="position: absolute; overflow: hidden; left: {lineNumbersWidth}px; width: {editorWidth}px; height: {editorHeight}px"
|
||||
>
|
||||
<div
|
||||
class="lines-content monaco-editor-background"
|
||||
style="position: absolute; overflow: hidden; width: 1.67772e+07px; height: 1.67772e+07px; transform: translate3d(0px, 0px, 0px); contain: strict; top: 0px; left: 0px;"
|
||||
>
|
||||
<div
|
||||
class="view-lines monaco-mouse-cursor-text text-tertiary/60"
|
||||
style="line-height: {lineHeight}px; position: absolute; font-family: {fontFamily}; font-weight: normal; font-size: {fontSize}px; font-feature-settings: 'liga' 0, 'calt' 0; font-variation-settings: normal; line-height: {lineHeight}px; letter-spacing: 0px; width: 1143px; height: 789px;"
|
||||
>
|
||||
{#each lines as line, i}
|
||||
<div
|
||||
style="height: {lineHeight}px; top: {i * lineHeight}px;"
|
||||
class="text-nowrap whitespace-pre"
|
||||
>
|
||||
{line}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -187,7 +187,7 @@
|
||||
`path` in module.value
|
||||
? module.value.path
|
||||
: ($initialPathStore == '' ? $pathStore : $initialPathStore) + '/' + module.id,
|
||||
jobKinds: ['preview', 'script', 'flowpreview', 'flow'].join(','),
|
||||
jobKinds: ['preview', 'script', 'flowpreview', 'flow', 'flowscript'].join(','),
|
||||
page: 1,
|
||||
perPage: 1
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { BROWSER } from 'esm-env'
|
||||
|
||||
import { editor as meditor } from 'monaco-editor'
|
||||
import '@codingame/monaco-vscode-standalone-languages'
|
||||
// import '@codingame/monaco-vscode-standalone-languages'
|
||||
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
|
||||
|
||||
@@ -515,6 +515,7 @@
|
||||
on:change={() => {
|
||||
dispatch('change', { argName, arg })
|
||||
}}
|
||||
loadAsync
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -583,6 +584,7 @@
|
||||
focused = false
|
||||
}}
|
||||
autoHeight
|
||||
loadAsync
|
||||
/>
|
||||
</div>
|
||||
{#if !hideHelpButton}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
export let error = ''
|
||||
export let editor: SimpleEditor | undefined = undefined
|
||||
export let small = false
|
||||
export let loadAsync = false
|
||||
|
||||
$: tooBig = code && code?.length > 1000000
|
||||
|
||||
@@ -38,6 +39,7 @@
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="border w-full">
|
||||
<SimpleEditor
|
||||
{loadAsync}
|
||||
{small}
|
||||
on:focus
|
||||
on:blur
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { Schema } from '$lib/common'
|
||||
import { ScriptService, type FlowModule, type Job, JobService } from '$lib/gen'
|
||||
import { ScriptService, type FlowModule, type Job } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { getScriptByPath } from '$lib/scripts'
|
||||
|
||||
import { CornerDownLeft, Loader2 } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
|
||||
import Button from './common/button/Button.svelte'
|
||||
import type { FlowEditorContext } from './flows/types'
|
||||
|
||||
@@ -68,17 +69,12 @@
|
||||
script.content,
|
||||
script.language,
|
||||
args,
|
||||
$flowStore?.tag ?? (val.tag_override ? val.tag_override : script.tag)
|
||||
$flowStore?.tag ?? (val.tag_override ? val.tag_override : script.tag),
|
||||
script.lock,
|
||||
val.hash ?? script.hash
|
||||
)
|
||||
} else if (val.type == 'flow') {
|
||||
await testJobLoader?.abstractRun(() =>
|
||||
JobService.runFlowByPath({
|
||||
workspace: $workspaceStore!,
|
||||
path: val.path,
|
||||
requestBody: args,
|
||||
skipPreprocessor: true
|
||||
})
|
||||
)
|
||||
await testJobLoader?.runFlowByPath(val.path, args)
|
||||
} else {
|
||||
throw Error('Not supported module type')
|
||||
}
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
resourceTypeInfo = resourceType
|
||||
if (resourceType.schema) {
|
||||
resourceSchema = resourceType.schema as Schema
|
||||
resourceSchema.order =
|
||||
resourceSchema.order ?? Object.keys(resourceSchema.properties).sort()
|
||||
}
|
||||
if (resourceTypeInfo?.format_extension) {
|
||||
textFileContent = args.content
|
||||
@@ -237,7 +239,7 @@
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<div>
|
||||
{#if loadingSchema}
|
||||
<Skeleton layout={[[4]]} />
|
||||
{:else if !viewJsonSchema && resourceSchema && resourceSchema?.properties}
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="flex flex-col w-full items-start">
|
||||
<div class="flex flex-col w-full items-start min-h-9">
|
||||
<div class="flex flex-row w-full items-center">
|
||||
{#if collection?.length > 0}
|
||||
<Select
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/common'
|
||||
import { CornerDownLeft, Loader2 } from 'lucide-svelte'
|
||||
|
||||
export let isLoading
|
||||
export let hideShortcut = false
|
||||
export let onRun: () => Promise<void>
|
||||
export let onCancel: () => Promise<void>
|
||||
</script>
|
||||
|
||||
{#if !isLoading}
|
||||
<Button
|
||||
loading={isLoading}
|
||||
size="sm"
|
||||
color="dark"
|
||||
btnClasses="!px-2 !py-1"
|
||||
on:click={() => onRun()}
|
||||
shortCut={{ Icon: CornerDownLeft, hide: hideShortcut }}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
size="sm"
|
||||
color="red"
|
||||
variant="border"
|
||||
btnClasses="!px-2 !py-1"
|
||||
on:click={() => onCancel()}
|
||||
>
|
||||
<Loader2 size={14} class="animate-spin mr-2" />
|
||||
Cancel
|
||||
</Button>
|
||||
{/if}
|
||||
@@ -92,7 +92,7 @@
|
||||
right: 'Enabled'
|
||||
}}
|
||||
on:change={async (e) => {
|
||||
if (!newItem) {
|
||||
if (!newItem && $initialPrimarySchedule != false) {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path: path,
|
||||
workspace: $workspaceStore ?? '',
|
||||
@@ -143,7 +143,7 @@
|
||||
<p class="text-xs text-tertiary mt-10">Define a schedule frequency first</p>
|
||||
{/if}
|
||||
|
||||
{#if $initialPrimarySchedule != false}
|
||||
{#if $initialPrimarySchedule != false && !newItem}
|
||||
<div class="flex">
|
||||
<Button size="xs" color="light" on:click={() => scheduleEditor?.openEdit(path, isFlow)}
|
||||
>Advanced</Button
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import type { ComponentCustomCSS } from './apps/types'
|
||||
|
||||
export let schema: Schema | any
|
||||
export let schemaSkippedValues: string[] = []
|
||||
export let hiddenArgs: string[] = []
|
||||
export let schemaFieldTooltip: Record<string, string> = {}
|
||||
export let args: Record<string, any> = {}
|
||||
export let disabledArgs: string[] = []
|
||||
@@ -204,7 +204,7 @@
|
||||
: ''}
|
||||
>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{#if !schemaSkippedValues.includes(argName) && keys.includes(argName)}
|
||||
{#if !hiddenArgs.includes(argName) && keys.includes(argName)}
|
||||
{#if typeof diff[argName] === 'object' && diff[argName].oldSchema}
|
||||
{@const formerProperty = diff[argName].oldSchema}
|
||||
<div class="px-2">
|
||||
@@ -278,6 +278,7 @@
|
||||
}}
|
||||
on:acceptChange={(e) => dispatch('acceptChange', e.detail)}
|
||||
on:rejectChange={(e) => dispatch('rejectChange', e.detail)}
|
||||
on:keydownCmdEnter={() => dispatch('keydownCmdEnter')}
|
||||
{disablePortal}
|
||||
{resourceTypes}
|
||||
{prettifyHeader}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user