Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1d83d38dd | ||
|
|
d9d0dafe45 | ||
|
|
18b5bc6bd3 | ||
|
|
60ff7d06b0 |
19
.github/workflows/backend-test.yml
vendored
19
.github/workflows/backend-test.yml
vendored
@@ -15,7 +15,7 @@ on:
|
||||
|
||||
jobs:
|
||||
cargo_test:
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: [self-hosted, new]
|
||||
container:
|
||||
image: ghcr.io/windmill-labs/backend-tests
|
||||
services:
|
||||
@@ -31,16 +31,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
# - uses: Swatinem/rust-cache@v2
|
||||
# with:
|
||||
# workspaces: |
|
||||
# backend
|
||||
# backend -> target
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: |
|
||||
backend
|
||||
backend -> target
|
||||
- name: cargo test
|
||||
timeout-minutes: 15
|
||||
timeout-minutes: 10
|
||||
run:
|
||||
mkdir frontend/build && cd backend && touch
|
||||
windmill-api/openapi-deref.yaml &&
|
||||
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill
|
||||
DISABLE_EMBEDDING=true cargo test --features enterprise --all --
|
||||
--nocapture
|
||||
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill DISABLE_EMBEDDING=true cargo
|
||||
test --features enterprise --all -- --nocapture
|
||||
|
||||
13
.github/workflows/benchmark.yml
vendored
13
.github/workflows/benchmark.yml
vendored
@@ -7,13 +7,14 @@ on:
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: [self-hosted, new]
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_DB: windmill
|
||||
POSTGRES_PASSWORD: changeme
|
||||
|
||||
options: >-
|
||||
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
|
||||
--health-retries 5
|
||||
@@ -34,12 +35,12 @@ jobs:
|
||||
image: ghcr.io/windmill-labs/windmill-ee:main
|
||||
env:
|
||||
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
|
||||
MODE: worker
|
||||
DISABLE_SERVER: true
|
||||
WORKER_GROUP: dedicated
|
||||
DEDICATED_WORKER: "admins:f/benchmarks/dedicated"
|
||||
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
|
||||
options: >-
|
||||
--pull always --restart unless-stopped
|
||||
--pull always
|
||||
steps:
|
||||
- uses: denoland/setup-deno@v1
|
||||
with:
|
||||
@@ -49,11 +50,9 @@ jobs:
|
||||
ref: benchmarks
|
||||
- name: benchmark
|
||||
timeout-minutes: 20
|
||||
run:
|
||||
deno run --unstable -A -r
|
||||
run: deno run --unstable -A -r
|
||||
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
|
||||
-c
|
||||
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
|
||||
-c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
|
||||
- name: Push changes
|
||||
run: |
|
||||
pwd
|
||||
|
||||
2
.github/workflows/change-versions.yml
vendored
2
.github/workflows/change-versions.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
- "version.txt"
|
||||
jobs:
|
||||
change_version:
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
container: node:18
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
13
.github/workflows/clean-docker.yml
vendored
Normal file
13
.github/workflows/clean-docker.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Clean docker
|
||||
on:
|
||||
schedule:
|
||||
# * is a special character in YAML so you have to quote this string
|
||||
- cron: "0 0 */2 * *"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted, new]
|
||||
steps:
|
||||
- name: clean docker
|
||||
run: |
|
||||
sudo docker system prune -f
|
||||
4
.github/workflows/deno_on_release.yml
vendored
4
.github/workflows/deno_on_release.yml
vendored
@@ -8,7 +8,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build_deno_and_push_to_repo:
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: generate_deno
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
tag_repo:
|
||||
needs: [build_deno_and_push_to_repo]
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
@@ -16,7 +16,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build_ee:
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
15
.github/workflows/docker-image.yml
vendored
15
.github/workflows/docker-image.yml
vendored
@@ -20,7 +20,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
org.opencontainers.image.licenses=AGPLv3
|
||||
|
||||
build_ee:
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
|
||||
deploy_s3:
|
||||
needs: [build_ee]
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request'
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
@@ -165,9 +165,10 @@ jobs:
|
||||
bucket: windmill-frontend
|
||||
bucket-region: us-east-1
|
||||
|
||||
|
||||
attach_amd64_binary_to_release:
|
||||
needs: [build, build_ee]
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
env:
|
||||
ARCH: amd64
|
||||
@@ -202,6 +203,7 @@ jobs:
|
||||
mv "${{ steps.extract.outputs.destination }}/windmill" "${{ steps.extract.outputs.destination }}/windmill-${ARCH}"
|
||||
mv "${{ steps.extract-ee.outputs.destination }}/windmill" "${{ steps.extract-ee.outputs.destination }}/windmill-ee-${ARCH}"
|
||||
|
||||
|
||||
- name: Attach binary to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
@@ -211,7 +213,7 @@ jobs:
|
||||
|
||||
attach_arm64_binary_to_release:
|
||||
needs: [build, build_ee]
|
||||
runs-on: ubicoud
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
env:
|
||||
ARCH: arm64
|
||||
@@ -246,6 +248,7 @@ jobs:
|
||||
mv "${{ steps.extract.outputs.destination }}/windmill" "${{ steps.extract.outputs.destination }}/windmill-${ARCH}"
|
||||
mv "${{ steps.extract-ee.outputs.destination }}/windmill" "${{ steps.extract-ee.outputs.destination }}/windmill-ee-${ARCH}"
|
||||
|
||||
|
||||
- name: Attach binary to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
@@ -255,7 +258,7 @@ jobs:
|
||||
|
||||
publish_ecr:
|
||||
needs: [build_ee]
|
||||
runs-on: ubicloud
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -5,6 +5,8 @@ env:
|
||||
name: Build windmill:mssql
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 */4 * *"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-mssql
|
||||
8
.github/workflows/frontend-check.yml
vendored
8
.github/workflows/frontend-check.yml
vendored
@@ -1,14 +1,14 @@
|
||||
name: check frontend build
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, closed]
|
||||
types: [opened,synchronize,reopened,closed]
|
||||
paths:
|
||||
- "frontend/**"
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
npm_check:
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
@@ -16,6 +16,4 @@ jobs:
|
||||
node-version: 18
|
||||
- name: "npm check"
|
||||
timeout-minutes: 2
|
||||
run:
|
||||
cd frontend && npm ci && npm run generate-backend-client && npm run
|
||||
check
|
||||
run: cd frontend && npm ci && npm run generate-backend-client && npm run check
|
||||
|
||||
2
.github/workflows/go_on_release.yml
vendored
2
.github/workflows/go_on_release.yml
vendored
@@ -10,7 +10,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build_go_and_push_to_repo:
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
|
||||
2
.github/workflows/npm_on_release.yml
vendored
2
.github/workflows/npm_on_release.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_npm:
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
|
||||
11
.github/workflows/publish_lsp.yml
vendored
11
.github/workflows/publish_lsp.yml
vendored
@@ -16,17 +16,14 @@ permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
sleep:
|
||||
runs-on: ubicloud
|
||||
publish_lsp:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sleep for 900 seconds waiting for pypi to update index
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: sleep 900
|
||||
shell: bash
|
||||
publish_lsp:
|
||||
needs: [sleep]
|
||||
runs-on: ubicloud
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
@@ -65,7 +62,7 @@ jobs:
|
||||
|
||||
publish_lsp_private:
|
||||
needs: [publish_lsp]
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
2
.github/workflows/pypi_on_release.yml
vendored
2
.github/workflows/pypi_on_release.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
publish_pypi:
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
container:
|
||||
image: ghcr.io/windmill-labs/python-client-builder
|
||||
|
||||
2
.github/workflows/release-please.yml
vendored
2
.github/workflows/release-please.yml
vendored
@@ -6,7 +6,7 @@ name: release-please
|
||||
jobs:
|
||||
release-please:
|
||||
name: "Release please"
|
||||
runs-on: ubicloud-standard-8
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: GoogleCloudPlatform/release-please-action@v3
|
||||
with:
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,34 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [1.217.0](https://github.com/windmill-labs/windmill/compare/v1.216.0...v1.217.0) (2023-11-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for raw query args ([8275602](https://github.com/windmill-labs/windmill/commit/82756023728fe392fee93a7bba0567ce582aad8c))
|
||||
* **frontend:** add "hide schedules" filter ([#2710](https://github.com/windmill-labs/windmill/issues/2710)) ([46e0f91](https://github.com/windmill-labs/windmill/commit/46e0f913878c9f688594cfe1c9184438e4facf49))
|
||||
* **frontend:** Added tailwind classes auto-complete ([#2712](https://github.com/windmill-labs/windmill/issues/2712)) ([2d3ce8a](https://github.com/windmill-labs/windmill/commit/2d3ce8a49c952accb4f1ceb0e6ea608df539f179))
|
||||
* **frontend:** Stat card improvement ([#2709](https://github.com/windmill-labs/windmill/issues/2709)) ([89abb68](https://github.com/windmill-labs/windmill/commit/89abb68f635d062778485c15d88fd6272ebd40a2))
|
||||
* scheduled app reports ([#2714](https://github.com/windmill-labs/windmill/issues/2714)) ([3789b34](https://github.com/windmill-labs/windmill/commit/3789b34dae72ff57bb9e0bb7e93439446c78095d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ai fix popup placement + update edit/fix prompt to return complete code ([#2715](https://github.com/windmill-labs/windmill/issues/2715)) ([76a387f](https://github.com/windmill-labs/windmill/commit/76a387f4a181d21afad0138c07f947aa292978cc))
|
||||
* better error for moved openai resource ([#2724](https://github.com/windmill-labs/windmill/issues/2724)) ([15b2c9f](https://github.com/windmill-labs/windmill/commit/15b2c9f171122ab074f8430a785f8ed577921fcc))
|
||||
* Deno can talk to private NPM registries behind HTTPS ([#2713](https://github.com/windmill-labs/windmill/issues/2713)) ([726866b](https://github.com/windmill-labs/windmill/commit/726866b410863ca9583a8145ebd9f4c4557cef08))
|
||||
* Error handler now supports flows ([#2707](https://github.com/windmill-labs/windmill/issues/2707)) ([36e46e2](https://github.com/windmill-labs/windmill/commit/36e46e2e47e1e949e1e235fa4ec0365b46d80de1))
|
||||
* **frontend:** fix separator z-index ([#2720](https://github.com/windmill-labs/windmill/issues/2720)) ([9525ab7](https://github.com/windmill-labs/windmill/commit/9525ab7bbab9f820d1f6fee44f9a2e9ad19d1b54))
|
||||
* **frontend:** Fix table initial ordering ([#2727](https://github.com/windmill-labs/windmill/issues/2727)) ([1a1d1db](https://github.com/windmill-labs/windmill/commit/1a1d1db96fd069434291f4b5f37ae1d0e21c5e44))
|
||||
* **frontend:** Improve error message + fix overflow when file name is too long ([#2691](https://github.com/windmill-labs/windmill/issues/2691)) ([c990f85](https://github.com/windmill-labs/windmill/commit/c990f856aaf821899cc54db20a653af396ceae7f))
|
||||
* generate cargo lock file ([#2722](https://github.com/windmill-labs/windmill/issues/2722)) ([bd31979](https://github.com/windmill-labs/windmill/commit/bd31979a62a30071e68385b5eb5dc28f37ea8fb5))
|
||||
* improve autocomplete reactivity ([c3eaf0b](https://github.com/windmill-labs/windmill/commit/c3eaf0bf4aab531b52a1b3ac276c6840b0925786))
|
||||
* make dedicated workers able to redeploy automatically ([e6d67f4](https://github.com/windmill-labs/windmill/commit/e6d67f4e5994360e7ae83c1303b31514b6062d1d))
|
||||
* minor fixes to private NPM and python registries to get everything working ([#2728](https://github.com/windmill-labs/windmill/issues/2728)) ([3d6fb15](https://github.com/windmill-labs/windmill/commit/3d6fb15a90f4d5aa18a6d6158760a380295e3d9e))
|
||||
* only list session and permanent token in user settings ([c8046af](https://github.com/windmill-labs/windmill/commit/c8046af9d01ade6891ea97e56974bb742bbf3e6e))
|
||||
* trim .bun.ts for local imports ([d2b3026](https://github.com/windmill-labs/windmill/commit/d2b3026032d288e1e5de1f37979d92d02094b3e4))
|
||||
|
||||
## [1.216.0](https://github.com/windmill-labs/windmill/compare/v1.215.0...v1.216.0) (2023-11-26)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1\n ORDER BY created_at DESC",
|
||||
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1\n ORDER BY created_at DESC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -48,5 +48,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "9ce6eecfa10c2f71cc536957ead498ad829ea8023ce449eab27225ec66738525"
|
||||
"hash": "773c145013623e4eb29a8df70e46d805dc5b15942a36a0d988521abb0cb34e41"
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND label != 'ephemeral-script'\n ORDER BY created_at DESC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "label",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token_prefix",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "expiration",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "last_used_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "scopes",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "bc1f2f169b4960dae02f62e37bb4ae081146e598109860ee1f42cd3778c5ebaf"
|
||||
}
|
||||
322
backend/Cargo.lock
generated
322
backend/Cargo.lock
generated
@@ -559,9 +559,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "atoi_simd"
|
||||
version = "0.15.5"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccfc14f5c3e34de57539a7ba9c18ecde3d9bbde48d232ea1da3e468adb307fd0"
|
||||
checksum = "fc41b65e01b6851bdcd2d741824e6b310d571396bf3915e31e4792034ee65126"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
@@ -571,11 +571,11 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-write-file"
|
||||
version = "0.1.1"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ae364a6c1301604bbc6dfbf8c385c47ff82301dd01eef506195a029196d8d04"
|
||||
checksum = "c232177ba50b16fe7a4588495bd474a62a9e45a8e4ca6fd7d0b7ac29d164631e"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
@@ -587,29 +587,29 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80c950a809d39bc9480207cb1cfc879ace88ea7e3a4392a8e9999e45d6e5692e"
|
||||
checksum = "8e245d7c741a8e4b23133f36750c4bcb3938a66ac49510caaf8b83afd52db1ec"
|
||||
dependencies = [
|
||||
"aws-credential-types 1.0.1",
|
||||
"aws-credential-types 1.0.0",
|
||||
"aws-http 0.60.0",
|
||||
"aws-runtime 1.0.1",
|
||||
"aws-runtime 1.0.0",
|
||||
"aws-sdk-sso",
|
||||
"aws-sdk-ssooidc",
|
||||
"aws-sdk-sts",
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-http 0.60.0",
|
||||
"aws-smithy-json 0.60.0",
|
||||
"aws-smithy-runtime 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-types 1.0.1",
|
||||
"aws-smithy-runtime 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"aws-types 1.0.0",
|
||||
"bytes",
|
||||
"fastrand 2.0.1",
|
||||
"hex",
|
||||
"http",
|
||||
"hyper",
|
||||
"ring 0.17.6",
|
||||
"ring 0.17.5",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -630,13 +630,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-credential-types"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1317e1a3514b103cf7d5828bbab3b4d30f56bd22d684f8568bc51b6cfbbb1c"
|
||||
checksum = "e6dec6f3d42983be70a113f999476185e124884f43f4d60129c7157aede7bda1"
|
||||
dependencies = [
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -663,9 +663,9 @@ version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "361c4310fdce94328cc2d1ca0c8a48c13f43009c61d3367585685a50ca8c66b6"
|
||||
dependencies = [
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-types 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"aws-types 1.0.0",
|
||||
"bytes",
|
||||
"http",
|
||||
"http-body",
|
||||
@@ -697,18 +697,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-runtime"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ed7ef604a15fd0d4d9e43701295161ea6b504b63c44990ead352afea2bc15e9"
|
||||
checksum = "36ba3ad97d674bfaeed684d528a07cee6e81cbf16e6d6c7e272a130b5e71e6b9"
|
||||
dependencies = [
|
||||
"aws-credential-types 1.0.1",
|
||||
"aws-credential-types 1.0.0",
|
||||
"aws-http 0.60.0",
|
||||
"aws-sigv4 1.0.1",
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-sigv4 1.0.0",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-http 0.60.0",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-types 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"aws-types 1.0.0",
|
||||
"fastrand 2.0.1",
|
||||
"http",
|
||||
"percent-encoding",
|
||||
@@ -748,20 +748,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.3.0"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0619ab97a5ca8982e7de073cdc66f93e5f6a1b05afc09e696bec1cb3607cd4df"
|
||||
checksum = "5786afe1fd164e53f108b2bd4982a31c5a821dc1677d05a12de65ebcc6ede52a"
|
||||
dependencies = [
|
||||
"aws-credential-types 1.0.1",
|
||||
"aws-credential-types 1.0.0",
|
||||
"aws-http 0.60.0",
|
||||
"aws-runtime 1.0.1",
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-runtime 1.0.0",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-http 0.60.0",
|
||||
"aws-smithy-json 0.60.0",
|
||||
"aws-smithy-runtime 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-types 1.0.1",
|
||||
"aws-smithy-runtime 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"aws-types 1.0.0",
|
||||
"bytes",
|
||||
"http",
|
||||
"regex",
|
||||
@@ -770,20 +770,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.3.0"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f04b9f5474cc0f35d829510b2ec8c21e352309b46bf9633c5a81fb9321e9b1c7"
|
||||
checksum = "31569ac7750ebc3097058c1e72d79576e16b3fb262aa0d6510188bff623f9804"
|
||||
dependencies = [
|
||||
"aws-credential-types 1.0.1",
|
||||
"aws-credential-types 1.0.0",
|
||||
"aws-http 0.60.0",
|
||||
"aws-runtime 1.0.1",
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-runtime 1.0.0",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-http 0.60.0",
|
||||
"aws-smithy-json 0.60.0",
|
||||
"aws-smithy-runtime 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-types 1.0.1",
|
||||
"aws-smithy-runtime 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"aws-types 1.0.0",
|
||||
"bytes",
|
||||
"http",
|
||||
"regex",
|
||||
@@ -792,22 +792,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.3.0"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5700da387716ccfc30b27f44b008f457e1baca5b0f05b6b95455778005e3432a"
|
||||
checksum = "9b1d955bacd8c3637908a40a4af2f7a732461ee7d95ec02599a3610ee55781d7"
|
||||
dependencies = [
|
||||
"aws-credential-types 1.0.1",
|
||||
"aws-credential-types 1.0.0",
|
||||
"aws-http 0.60.0",
|
||||
"aws-runtime 1.0.1",
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-runtime 1.0.0",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-http 0.60.0",
|
||||
"aws-smithy-json 0.60.0",
|
||||
"aws-smithy-query",
|
||||
"aws-smithy-runtime 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-runtime 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"aws-smithy-xml 0.60.0",
|
||||
"aws-types 1.0.1",
|
||||
"aws-types 1.0.0",
|
||||
"http",
|
||||
"regex",
|
||||
"tracing",
|
||||
@@ -833,7 +833,7 @@ dependencies = [
|
||||
"p256",
|
||||
"percent-encoding",
|
||||
"regex",
|
||||
"ring 0.17.6",
|
||||
"ring 0.17.5",
|
||||
"sha2 0.10.8",
|
||||
"time",
|
||||
"tracing",
|
||||
@@ -842,14 +842,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sigv4"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380adcc8134ad8bbdfeb2ace7626a869914ee266322965276cbc54066186d236"
|
||||
checksum = "b4d07e2f2fc32acb7423d054ec8ba2b361dafc95fabc5a211baf4a566571d20e"
|
||||
dependencies = [
|
||||
"aws-credential-types 1.0.1",
|
||||
"aws-credential-types 1.0.0",
|
||||
"aws-smithy-http 0.60.0",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"hex",
|
||||
@@ -876,9 +876,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-async"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e37ca17d25fe1e210b6d4bdf59b81caebfe99f986201a1228cb5061233b4b13"
|
||||
checksum = "1fbfa248f7f966d73e325dbc85851a5500042b6d96e3c3b535a8527707f36fe4"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
@@ -944,8 +944,8 @@ version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b1de8aee22f67de467b2e3d0dd0fb30859dc53f579a63bd5381766b987db644"
|
||||
dependencies = [
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"bytes",
|
||||
"bytes-utils",
|
||||
"futures-core",
|
||||
@@ -973,7 +973,7 @@ version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a46dd338dc9576d6a6a5b5a19bd678dcad018ececee11cf28ecd7588bd1a55c"
|
||||
dependencies = [
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-types 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -982,7 +982,7 @@ version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "feb5b8c7a86d4b6399169670723b7e6f21a39fc833a30f5c5a2f997608178129"
|
||||
dependencies = [
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
@@ -1012,17 +1012,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-runtime"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "273479291efc55e7b0bce985b139d86b6031adb8e50f65c1f712f20ba38f6388"
|
||||
checksum = "064b808143d80b50744b1b22cce801238a545b84859c6cf8e275997252dd1d25"
|
||||
dependencies = [
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-http 0.60.0",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"bytes",
|
||||
"fastrand 2.0.1",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
@@ -1052,12 +1051,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-runtime-api"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6cebff0d977b6b6feed2fd07db52aac58ba3ccaf26cdd49f1af4add5061bef9"
|
||||
checksum = "4d27c3235d4972ed976b5c1a82286e7c4457f618f3c2ae6d4ae44f081dd24575"
|
||||
dependencies = [
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"bytes",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
@@ -1091,9 +1090,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-types"
|
||||
version = "1.0.2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7f48b3f27ddb40ab19892a5abda331f403e3cb877965e4e51171447807104af"
|
||||
checksum = "d2fc32035dc0636a8583cf0c6dd7f1e6d5404103b836d26228b8730907a88d9f"
|
||||
dependencies = [
|
||||
"base64-simd",
|
||||
"bytes",
|
||||
@@ -1145,14 +1144,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-types"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8403fc56b1f3761e8efe45771ddc1165e47ec3417c68e68a4519b5cb030159ca"
|
||||
checksum = "b18c0eb301cce69298555c4884795497c67b3bd511aa3f07470382f4bf3ef043"
|
||||
dependencies = [
|
||||
"aws-credential-types 1.0.1",
|
||||
"aws-smithy-async 1.0.2",
|
||||
"aws-smithy-runtime-api 1.0.2",
|
||||
"aws-smithy-types 1.0.2",
|
||||
"aws-credential-types 1.0.0",
|
||||
"aws-smithy-async 1.0.1",
|
||||
"aws-smithy-runtime-api 1.0.1",
|
||||
"aws-smithy-types 1.0.1",
|
||||
"http",
|
||||
"rustc_version 0.4.0",
|
||||
"tracing",
|
||||
@@ -1770,9 +1769,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.10"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
|
||||
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -1780,9 +1779,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.9"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
|
||||
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -2068,7 +2067,7 @@ dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"memoffset 0.9.0",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
@@ -2853,12 +2852,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4005,9 +4004,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.66"
|
||||
version = "0.3.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -4445,6 +4444,15 @@ dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
@@ -4655,6 +4663,19 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.7.1",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
@@ -4819,7 +4840,7 @@ dependencies = [
|
||||
"quick-xml 0.31.0",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"ring 0.17.6",
|
||||
"ring 0.17.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"snafu",
|
||||
@@ -5293,7 +5314,7 @@ checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||
dependencies = [
|
||||
"der 0.7.8",
|
||||
"pkcs8 0.10.2",
|
||||
"spki 0.7.3",
|
||||
"spki 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5313,7 +5334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der 0.7.8",
|
||||
"spki 0.7.3",
|
||||
"spki 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6431,9 +6452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.6"
|
||||
version = "0.17.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866"
|
||||
checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"getrandom 0.2.11",
|
||||
@@ -6494,9 +6515,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.5"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af6c4b23d99685a1408194da11270ef8e9809aff951cc70ec9b17350b087e474"
|
||||
checksum = "6a3211b01eea83d80687da9eef70e39d65144a3894866a5153a2723e425a157f"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest 0.10.7",
|
||||
@@ -6507,7 +6528,7 @@ dependencies = [
|
||||
"pkcs8 0.10.2",
|
||||
"rand_core 0.6.4",
|
||||
"signature 2.2.0",
|
||||
"spki 0.7.3",
|
||||
"spki 0.7.2",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -6667,7 +6688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring 0.17.6",
|
||||
"ring 0.17.5",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
@@ -6699,7 +6720,7 @@ version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring 0.17.6",
|
||||
"ring 0.17.5",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
@@ -6877,7 +6898,7 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||
dependencies = [
|
||||
"ring 0.17.6",
|
||||
"ring 0.17.5",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
@@ -7394,9 +7415,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der 0.7.8",
|
||||
@@ -7657,7 +7678,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"rsa 0.9.5",
|
||||
"rsa 0.9.4",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2 0.10.8",
|
||||
@@ -8281,9 +8302,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.29.11"
|
||||
version = "0.29.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666"
|
||||
checksum = "0a18d114d420ada3a891e6bc8e96a2023402203296a47cdd65083377dad18ba5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
@@ -9371,9 +9392,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -9381,9 +9402,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
@@ -9396,9 +9417,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.39"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
|
||||
checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -9408,9 +9429,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -9418,9 +9439,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -9431,15 +9452,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.39"
|
||||
version = "0.3.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403"
|
||||
checksum = "c6433b7c56db97397842c46b67e11873eda263170afeb3a2dc74a7cb370fee0d"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
@@ -9451,9 +9472,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.39"
|
||||
version = "0.3.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89"
|
||||
checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -9484,9 +9505,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.66"
|
||||
version = "0.3.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
|
||||
checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -9498,7 +9519,7 @@ version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
|
||||
dependencies = [
|
||||
"ring 0.17.6",
|
||||
"ring 0.17.5",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
@@ -9572,7 +9593,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -9607,7 +9628,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -9679,7 +9700,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"chrono",
|
||||
@@ -9697,7 +9718,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -9710,7 +9731,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -9741,7 +9762,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -9749,7 +9770,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -9760,7 +9781,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -9772,7 +9793,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -9783,7 +9804,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.12.0",
|
||||
@@ -9794,7 +9815,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -9811,7 +9832,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -9822,7 +9843,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -9839,7 +9860,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wasm"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.11",
|
||||
@@ -9857,7 +9878,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -9889,7 +9910,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -9901,7 +9922,6 @@ dependencies = [
|
||||
"deno_console",
|
||||
"deno_core",
|
||||
"deno_fetch",
|
||||
"deno_tls",
|
||||
"deno_url",
|
||||
"deno_web",
|
||||
"deno_webidl",
|
||||
@@ -9916,7 +9936,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"mysql_async",
|
||||
"native-tls",
|
||||
"nix",
|
||||
"nix 0.27.1",
|
||||
"once_cell",
|
||||
"pem 3.0.2",
|
||||
"postgres-native-tls",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -21,7 +21,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -98,7 +98,7 @@ hyper = { version = "^0", features = ["full"] }
|
||||
tokio = { version = "^1", features = ["full", "tracing"] }
|
||||
tower = "^0"
|
||||
tower-http = { version = "^0.4", features = ["trace", "cors"] }
|
||||
tower-cookies = "0.9.0"
|
||||
tower-cookies = "^0"
|
||||
serde = "^1"
|
||||
serde_json = { version = "^1", features = ["preserve_order", "raw_value"] }
|
||||
uuid = { version = "^1", features = ["serde", "v4"] }
|
||||
@@ -137,7 +137,6 @@ json-pointer = "^0"
|
||||
itertools = "^0"
|
||||
regex = "^1"
|
||||
deno_fetch = "0.139.0"
|
||||
deno_tls = "0.102.0"
|
||||
deno_console = "0.115.0"
|
||||
deno_url = "0.115.0"
|
||||
deno_webidl = "0.115.0"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
-- Add down migration script here
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add up migration script here
|
||||
INSERT INTO config (name, config) VALUES
|
||||
('worker__reports', '{"init_bash": "apt-get update\napt-get install -y chromium", "worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "chromium"]}') ON CONFLICT DO NOTHING;
|
||||
@@ -719,7 +719,7 @@ async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
&db,
|
||||
&job.workspace_id,
|
||||
&job.permissioned_as,
|
||||
"ephemeral-script",
|
||||
"ephemeral-zombie-jobs",
|
||||
*SCRIPT_TOKEN_EXPIRY,
|
||||
&job.email,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.217.0
|
||||
version: 1.216.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -1634,11 +1634,6 @@ paths:
|
||||
operationId: listTokens
|
||||
tags:
|
||||
- user
|
||||
parameters:
|
||||
- name: exclude_ephemeral
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: truncated token
|
||||
@@ -2197,8 +2192,7 @@ paths:
|
||||
|
||||
/w/{workspace}/resources/get_value_interpolated/{path}:
|
||||
get:
|
||||
summary:
|
||||
get resource interpolated (variables and resources are fully unrolled)
|
||||
summary: get resource interpolated (variables and resources are fully unrolled)
|
||||
operationId: getResourceValueInterpolated
|
||||
tags:
|
||||
- resource
|
||||
@@ -2886,8 +2880,7 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
- name: first_parent_hash
|
||||
description:
|
||||
mask to filter scripts whom first direct parent has exact hash
|
||||
description: mask to filter scripts whom first direct parent has exact hash
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
@@ -3082,8 +3075,7 @@ paths:
|
||||
|
||||
/workers/custom_tags:
|
||||
get:
|
||||
summary:
|
||||
get all instance custom tags (tags are used to dispatch jobs to
|
||||
summary: get all instance custom tags (tags are used to dispatch jobs to
|
||||
different worker groups)
|
||||
operationId: getCustomTags
|
||||
tags:
|
||||
@@ -3134,8 +3126,7 @@ paths:
|
||||
|
||||
/w/{workspace}/scripts/delete/h/{hash}:
|
||||
post:
|
||||
summary:
|
||||
delete script by hash (erase content but keep hash, require admin)
|
||||
summary: delete script by hash (erase content but keep hash, require admin)
|
||||
operationId: deleteScriptByHash
|
||||
tags:
|
||||
- script
|
||||
@@ -3327,16 +3318,14 @@ paths:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: scheduled_in_secs
|
||||
description:
|
||||
schedule the script to execute in the number of seconds starting now
|
||||
description: schedule the script to execute in the number of seconds starting now
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
- $ref: "#/components/parameters/ParentJob"
|
||||
- $ref: "#/components/parameters/NewJobId"
|
||||
- name: invisible_to_owner
|
||||
description:
|
||||
make the run invisible to the the script owner (default false)
|
||||
description: make the run invisible to the the script owner (default false)
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -4309,8 +4298,7 @@ paths:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: scheduled_in_secs
|
||||
description:
|
||||
schedule the script to execute in the number of seconds starting now
|
||||
description: schedule the script to execute in the number of seconds starting now
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -4318,8 +4306,7 @@ paths:
|
||||
- $ref: "#/components/parameters/NewJobId"
|
||||
- $ref: "#/components/parameters/IncludeHeader"
|
||||
- name: invisible_to_owner
|
||||
description:
|
||||
make the run invisible to the the flow owner (default false)
|
||||
description: make the run invisible to the the flow owner (default false)
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -4371,8 +4358,7 @@ paths:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: scheduled_in_secs
|
||||
description:
|
||||
schedule the script to execute in the number of seconds starting now
|
||||
description: schedule the script to execute in the number of seconds starting now
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -4380,8 +4366,7 @@ paths:
|
||||
- $ref: "#/components/parameters/NewJobId"
|
||||
- $ref: "#/components/parameters/IncludeHeader"
|
||||
- name: invisible_to_owner
|
||||
description:
|
||||
make the run invisible to the the flow owner (default false)
|
||||
description: make the run invisible to the the flow owner (default false)
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -4419,8 +4404,7 @@ paths:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: scheduled_in_secs
|
||||
description:
|
||||
schedule the script to execute in the number of seconds starting now
|
||||
description: schedule the script to execute in the number of seconds starting now
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -4428,8 +4412,7 @@ paths:
|
||||
- $ref: "#/components/parameters/NewJobId"
|
||||
- $ref: "#/components/parameters/IncludeHeader"
|
||||
- name: invisible_to_owner
|
||||
description:
|
||||
make the run invisible to the the script owner (default false)
|
||||
description: make the run invisible to the the script owner (default false)
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -4460,8 +4443,7 @@ paths:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/IncludeHeader"
|
||||
- name: invisible_to_owner
|
||||
description:
|
||||
make the run invisible to the the script owner (default false)
|
||||
description: make the run invisible to the the script owner (default false)
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -4494,8 +4476,7 @@ paths:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/IncludeHeader"
|
||||
- name: invisible_to_owner
|
||||
description:
|
||||
make the run invisible to the the script owner (default false)
|
||||
description: make the run invisible to the the script owner (default false)
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -4710,8 +4691,7 @@ paths:
|
||||
- job
|
||||
responses:
|
||||
"200":
|
||||
description:
|
||||
the timestamp of the db that can be used to compute the drift
|
||||
description: the timestamp of the db that can be used to compute the drift
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -4947,8 +4927,7 @@ paths:
|
||||
|
||||
/w/{workspace}/jobs/resume_urls/{id}/{resume_id}:
|
||||
get:
|
||||
summary:
|
||||
get resume urls given a job_id, resume_id and a nonce to resume a flow
|
||||
summary: get resume urls given a job_id, resume_id and a nonce to resume a flow
|
||||
operationId: getResumeUrls
|
||||
tags:
|
||||
- job
|
||||
@@ -5499,8 +5478,7 @@ paths:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- name: only_member_of
|
||||
in: query
|
||||
description:
|
||||
only list the groups the user is member of (default false)
|
||||
description: only list the groups the user is member of (default false)
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
@@ -5688,8 +5666,7 @@ paths:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- name: only_member_of
|
||||
in: query
|
||||
description:
|
||||
only list the folders the user is member of (default false)
|
||||
description: only list the folders the user is member of (default false)
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
@@ -6590,8 +6567,7 @@ components:
|
||||
type: integer
|
||||
PerPage:
|
||||
name: per_page
|
||||
description:
|
||||
number of items to return for a given page (default 30, max 100)
|
||||
description: number of items to return for a given page (default 30, max 100)
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -6725,8 +6701,7 @@ components:
|
||||
type: boolean
|
||||
ArgsFilter:
|
||||
name: args
|
||||
description:
|
||||
filter on jobs containing those args as a json subset (@> in postgres)
|
||||
description: filter on jobs containing those args as a json subset (@> in postgres)
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
@@ -6738,8 +6713,7 @@ components:
|
||||
type: string
|
||||
ResultFilter:
|
||||
name: result
|
||||
description:
|
||||
filter on jobs containing those result as a json subset (@> in postgres)
|
||||
description: filter on jobs containing those result as a json subset (@> in postgres)
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
|
||||
@@ -102,13 +102,9 @@ async fn proxy(
|
||||
&openai_resource_path,
|
||||
&w_id
|
||||
)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
create_openai_json_error(format!(
|
||||
"Could not find the OpenAI resource at path {openai_resource_path}, update the resource path in the workspace settings"
|
||||
))
|
||||
})?;
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
if resource.is_none() {
|
||||
return Err(create_openai_json_error(
|
||||
|
||||
@@ -2127,37 +2127,19 @@ async fn impersonate(
|
||||
Ok((StatusCode::CREATED, token))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ListTokenQuery {
|
||||
exclude_ephemeral: Option<bool>,
|
||||
}
|
||||
|
||||
async fn list_tokens(
|
||||
Extension(db): Extension<DB>,
|
||||
ApiAuthed { email, .. }: ApiAuthed,
|
||||
Query(query): Query<ListTokenQuery>,
|
||||
) -> JsonResult<Vec<TruncatedToken>> {
|
||||
let rows = if query.exclude_ephemeral.unwrap_or(false) {
|
||||
sqlx::query_as!(
|
||||
TruncatedToken,
|
||||
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, \
|
||||
last_used_at, scopes FROM token WHERE email = $1 AND label != 'ephemeral-script'
|
||||
ORDER BY created_at DESC",
|
||||
email,
|
||||
)
|
||||
.fetch_all(&db)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as!(
|
||||
TruncatedToken,
|
||||
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, \
|
||||
last_used_at, scopes FROM token WHERE email = $1
|
||||
ORDER BY created_at DESC",
|
||||
email,
|
||||
)
|
||||
.fetch_all(&db)
|
||||
.await?
|
||||
};
|
||||
let rows = sqlx::query_as!(
|
||||
TruncatedToken,
|
||||
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, \
|
||||
last_used_at, scopes FROM token WHERE email = $1
|
||||
ORDER BY created_at DESC",
|
||||
email,
|
||||
)
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
use windmill_common::{
|
||||
error::{self, Error},
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
users::SUPERADMIN_SECRET_EMAIL,
|
||||
DB,
|
||||
};
|
||||
|
||||
pub async fn require_super_admin(db: &DB, email: &str) -> error::Result<()> {
|
||||
if email == SUPERADMIN_SECRET_EMAIL || email == SUPERADMIN_NOTIFICATION_EMAIL {
|
||||
if email == SUPERADMIN_SECRET_EMAIL {
|
||||
return Ok(());
|
||||
}
|
||||
let is_admin = sqlx::query_scalar!("SELECT super_admin FROM password WHERE email = $1", email)
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
pub const SUPERADMIN_SECRET_EMAIL: &str = "superadmin_secret@windmill.dev";
|
||||
pub const SUPERADMIN_NOTIFICATION_EMAIL: &str = "superadmin_notification@windmill.dev";
|
||||
|
||||
pub fn username_to_permissioned_as(user: &str) -> String {
|
||||
if user.contains('@') {
|
||||
|
||||
@@ -55,7 +55,7 @@ use windmill_common::{
|
||||
oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH,
|
||||
schedule::Schedule,
|
||||
scripts::{ScriptHash, ScriptLang},
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
users::SUPERADMIN_SECRET_EMAIL,
|
||||
worker::{to_raw_value, WORKER_CONFIG},
|
||||
DB, METRICS_ENABLED,
|
||||
};
|
||||
@@ -2095,8 +2095,8 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection
|
||||
job_payload: JobPayload,
|
||||
args: T,
|
||||
user: &str,
|
||||
mut email: &str,
|
||||
mut permissioned_as: String,
|
||||
email: &str,
|
||||
permissioned_as: String,
|
||||
scheduled_for_o: Option<chrono::DateTime<chrono::Utc>>,
|
||||
schedule_path: Option<String>,
|
||||
parent_job: Option<Uuid>,
|
||||
@@ -2249,10 +2249,6 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection
|
||||
priority,
|
||||
),
|
||||
JobPayload::ScriptHub { path } => {
|
||||
if path == "hub/7771/slack" {
|
||||
permissioned_as = SUPERADMIN_NOTIFICATION_EMAIL.to_string();
|
||||
email = SUPERADMIN_NOTIFICATION_EMAIL;
|
||||
}
|
||||
(
|
||||
None,
|
||||
Some(path),
|
||||
|
||||
@@ -60,7 +60,6 @@ deno_console.workspace = true
|
||||
deno_url.workspace = true
|
||||
deno_core.workspace = true
|
||||
deno_ast.workspace = true
|
||||
deno_tls.workspace = true
|
||||
postgres-native-tls.workspace = true
|
||||
native-tls.workspace = true
|
||||
mysql_async.workspace = true
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
start_child_process, write_file, write_file_binary,
|
||||
},
|
||||
AuthedClientBackgroundTask, BUN_CACHE_DIR, BUN_PATH, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV,
|
||||
NSJAIL_PATH, PATH_ENV, TZ_ENV,
|
||||
NPM_CONFIG_REGISTRY, NSJAIL_PATH, PATH_ENV, TZ_ENV,
|
||||
};
|
||||
|
||||
use tokio::{
|
||||
@@ -512,7 +512,7 @@ plugin(p)
|
||||
}
|
||||
|
||||
pub async fn get_common_bun_proc_envs(base_internal_url: &str) -> HashMap<String, String> {
|
||||
let bun_envs: HashMap<String, String> = HashMap::from([
|
||||
let mut bun_envs: HashMap<String, String> = HashMap::from([
|
||||
(String::from("PATH"), PATH_ENV.clone()),
|
||||
(String::from("HOME"), HOME_ENV.clone()),
|
||||
(String::from("TZ"), TZ_ENV.clone()),
|
||||
@@ -529,6 +529,10 @@ pub async fn get_common_bun_proc_envs(base_internal_url: &str) -> HashMap<String
|
||||
BUN_CACHE_DIR.to_string(),
|
||||
),
|
||||
]);
|
||||
|
||||
if let Some(ref s) = NPM_CONFIG_REGISTRY.read().await.clone() {
|
||||
bun_envs.insert(String::from("NPM_CONFIG_REGISTRY"), s.clone());
|
||||
}
|
||||
return bun_envs;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ lazy_static::lazy_static! {
|
||||
.map(|x| format!(";{x}"))
|
||||
.unwrap_or_else(|| String::new());
|
||||
|
||||
static ref DENO_CERT: String = std::env::var("DENO_CERT").ok().unwrap_or_else(|| String::new());
|
||||
static ref DENO_TLS_CA_STORE: String = std::env::var("DENO_TLS_CA_STORE").ok().unwrap_or_else(|| String::new());
|
||||
|
||||
}
|
||||
async fn get_common_deno_proc_envs(
|
||||
@@ -72,12 +70,6 @@ async fn get_common_deno_proc_envs(
|
||||
if let Some(ref s) = NPM_CONFIG_REGISTRY.read().await.clone() {
|
||||
deno_envs.insert(String::from("NPM_CONFIG_REGISTRY"), s.clone());
|
||||
}
|
||||
if DENO_CERT.len() > 0 {
|
||||
deno_envs.insert(String::from("DENO_CERT"), DENO_CERT.clone());
|
||||
}
|
||||
if DENO_TLS_CA_STORE.len() > 0 {
|
||||
deno_envs.insert(String::from("DENO_TLS_CA_STORE"), DENO_TLS_CA_STORE.clone());
|
||||
}
|
||||
return deno_envs;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,17 +6,16 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc, env, io::{BufReader, self}};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
|
||||
|
||||
use deno_ast::{ParseParams, SourceTextInfo};
|
||||
use deno_core::{
|
||||
op, serde_v8,
|
||||
v8::IsolateHandle,
|
||||
v8::{self},
|
||||
Extension, JsRuntime, Op, OpState, RuntimeOptions, Snapshot, error::AnyError,
|
||||
Extension, JsRuntime, Op, OpState, RuntimeOptions, Snapshot,
|
||||
};
|
||||
use deno_fetch::FetchPermissions;
|
||||
use deno_tls::{rustls::RootCertStore, rustls_pemfile};
|
||||
use deno_web::{BlobStore, TimersPermission};
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
@@ -39,33 +38,6 @@ pub struct IdContext {
|
||||
pub previous_id: String,
|
||||
}
|
||||
|
||||
pub struct ContainerRootCertStoreProvider {
|
||||
root_cert_store: RootCertStore,
|
||||
}
|
||||
|
||||
impl ContainerRootCertStoreProvider {
|
||||
fn new() -> ContainerRootCertStoreProvider {
|
||||
return ContainerRootCertStoreProvider {
|
||||
root_cert_store: deno_tls::create_default_root_cert_store(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_certificate(&mut self, cert_path: String) -> io::Result<()> {
|
||||
let cert_file = std::fs::File::open(cert_path)?;
|
||||
let mut reader = BufReader::new(cert_file);
|
||||
let pem_file = rustls_pemfile::certs(&mut reader)?;
|
||||
|
||||
self.root_cert_store.add_parsable_certificates(&pem_file);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl deno_tls::RootCertStoreProvider for ContainerRootCertStoreProvider {
|
||||
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> {
|
||||
Ok(&self.root_cert_store)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PermissionsContainer;
|
||||
|
||||
impl FetchPermissions for PermissionsContainer {
|
||||
@@ -560,18 +532,6 @@ pub async fn eval_fetch_timeout(
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let deno_fetch_options = if let Some(cert_path) = env::var("DENO_CERT").ok() {
|
||||
let mut cert_store_provider = ContainerRootCertStoreProvider::new();
|
||||
cert_store_provider.add_certificate(cert_path)?;
|
||||
|
||||
deno_fetch::Options {
|
||||
root_cert_store_provider: Some(Arc::new(cert_store_provider)),
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let exts: Vec<Extension> = vec![
|
||||
deno_webidl::deno_webidl::init_ops(),
|
||||
deno_url::deno_url::init_ops(),
|
||||
@@ -580,7 +540,9 @@ pub async fn eval_fetch_timeout(
|
||||
Arc::new(BlobStore::default()),
|
||||
None,
|
||||
),
|
||||
deno_fetch::deno_fetch::init_ops::<PermissionsContainer>(deno_fetch_options),
|
||||
deno_fetch::deno_fetch::init_ops::<PermissionsContainer>(
|
||||
Default::default(),
|
||||
),
|
||||
ext
|
||||
];
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ lazy_static::lazy_static! {
|
||||
|
||||
static ref PIP_INDEX_URL: Option<String> = std::env::var("PIP_INDEX_URL").ok();
|
||||
static ref PIP_TRUSTED_HOST: Option<String> = std::env::var("PIP_TRUSTED_HOST").ok();
|
||||
static ref PIP_INDEX_CERT: Option<String> = std::env::var("PIP_INDEX_CERT").ok();
|
||||
|
||||
|
||||
static ref RELATIVE_IMPORT_REGEX: Regex = Regex::new(r#"(import|from)\s(((u|f)\.)|\.)"#).unwrap();
|
||||
@@ -122,9 +121,6 @@ pub async fn pip_compile(
|
||||
if let Some(host) = PIP_TRUSTED_HOST.as_ref() {
|
||||
args.extend(["--trusted-host", host]);
|
||||
}
|
||||
if let Some(cert_path) = PIP_INDEX_CERT.as_ref() {
|
||||
args.extend(["--cert", cert_path]);
|
||||
}
|
||||
|
||||
let mut child_cmd = Command::new("pip-compile");
|
||||
child_cmd
|
||||
@@ -609,9 +605,6 @@ pub async fn handle_python_reqs(
|
||||
if let Some(url) = PIP_INDEX_URL.as_ref() {
|
||||
vars.push(("INDEX_URL", url));
|
||||
}
|
||||
if let Some(cert_path) = PIP_INDEX_CERT.as_ref() {
|
||||
vars.push(("PIP_INDEX_CERT", cert_path));
|
||||
}
|
||||
if let Some(host) = PIP_TRUSTED_HOST.as_ref() {
|
||||
vars.push(("TRUSTED_HOST", host));
|
||||
}
|
||||
@@ -711,9 +704,6 @@ pub async fn handle_python_reqs(
|
||||
if let Some(url) = PIP_INDEX_URL.as_ref() {
|
||||
command_args.extend(["--index-url", url]);
|
||||
}
|
||||
if let Some(cert_path) = PIP_INDEX_CERT.as_ref() {
|
||||
command_args.extend(["--cert", cert_path]);
|
||||
}
|
||||
if let Some(host) = PIP_TRUSTED_HOST.as_ref() {
|
||||
command_args.extend(["--trusted-host", &host]);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ use windmill_common::{
|
||||
flows::{FlowModule, FlowModuleValue, FlowValue},
|
||||
jobs::{JobKind, QueuedJob},
|
||||
scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang},
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
users::SUPERADMIN_SECRET_EMAIL,
|
||||
utils::{rd_string, StripPath},
|
||||
worker::{
|
||||
to_raw_value, to_raw_value_owned, update_ping, CLOUD_HOSTED, WORKER_CONFIG, WORKER_GROUP,
|
||||
@@ -142,8 +142,7 @@ pub async fn create_token_for_owner(
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
.unwrap_or(false)
|
||||
|| email == SUPERADMIN_SECRET_EMAIL
|
||||
|| email == SUPERADMIN_NOTIFICATION_EMAIL;
|
||||
|| email == SUPERADMIN_SECRET_EMAIL;
|
||||
|
||||
sqlx::query_scalar!(
|
||||
"INSERT INTO token
|
||||
@@ -958,7 +957,6 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
let worker_flow_transition_duration2 = worker_flow_transition_duration.clone();
|
||||
|
||||
let worker_name2 = worker_name.clone();
|
||||
let killpill_tx2 = killpill_tx.clone();
|
||||
let send_result = tokio::spawn(async move {
|
||||
while let Some(jc) = job_completed_rx.recv().await {
|
||||
if let Some(wj) = worker_job_completed_channel_queue2.as_ref() {
|
||||
@@ -970,6 +968,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
let same_worker_tx2 = same_worker_tx2.clone();
|
||||
let rsmq2 = rsmq2.clone();
|
||||
let worker_name = worker_name2.clone();
|
||||
|
||||
if matches!(jc.job.job_kind, JobKind::Noop) || is_dedicated_worker {
|
||||
thread_count.fetch_add(1, Ordering::SeqCst);
|
||||
let thread_count = thread_count.clone();
|
||||
@@ -993,7 +992,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
let worker_save_completed_job_duration2 =
|
||||
worker_save_completed_job_duration.clone();
|
||||
let worker_flow_transition_duration2 = worker_flow_transition_duration.clone();
|
||||
let killpill_tx = killpill_tx2.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
#[cfg(feature = "benchmark")]
|
||||
let process_start = Instant::now();
|
||||
@@ -1056,7 +1055,6 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
.execute(&db2)
|
||||
.await
|
||||
.expect("update config to trigger restart of all dedicated workers at that config");
|
||||
killpill_tx.send(()).unwrap_or_default();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -1107,71 +1105,79 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
let is_flow_worker;
|
||||
if let Some(flow_path) = _wp.path.strip_prefix("flow/") {
|
||||
is_flow_worker = true;
|
||||
let value = sqlx::query_scalar!(
|
||||
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
flow_path,
|
||||
_wp.workspace_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await;
|
||||
if let Ok(v) = value {
|
||||
if let Some(v) = v {
|
||||
let value = serde_json::from_value::<FlowValue>(v).map_err(|err| {
|
||||
Error::InternalErr(format!(
|
||||
"could not convert json to flow for {flow_path}: {err:?}"
|
||||
))
|
||||
});
|
||||
if let Ok(flow) = value {
|
||||
let workers = spawn_dedicated_workers_for_flow(
|
||||
&flow.modules,
|
||||
&_wp.path,
|
||||
&_wp.workspace_id,
|
||||
killpill_tx.clone(),
|
||||
&killpill_rx,
|
||||
db,
|
||||
&worker_dir,
|
||||
base_internal_url,
|
||||
&worker_name,
|
||||
&job_completed_tx,
|
||||
)
|
||||
.await;
|
||||
workers.into_iter().for_each(|(path, sender, handle)| {
|
||||
dedicated_handles.push(handle);
|
||||
hm.insert(path, sender);
|
||||
loop {
|
||||
let value = sqlx::query_scalar!(
|
||||
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
flow_path,
|
||||
_wp.workspace_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await;
|
||||
if let Ok(v) = value {
|
||||
if let Some(v) = v {
|
||||
let value = serde_json::from_value::<FlowValue>(v).map_err(|err| {
|
||||
Error::InternalErr(format!(
|
||||
"could not convert json to flow for {flow_path}: {err:?}"
|
||||
))
|
||||
});
|
||||
if let Ok(flow) = value {
|
||||
let workers = spawn_dedicated_workers_for_flow(
|
||||
&flow.modules,
|
||||
&_wp.path,
|
||||
&_wp.workspace_id,
|
||||
killpill_tx.clone(),
|
||||
&killpill_rx,
|
||||
db,
|
||||
&worker_dir,
|
||||
base_internal_url,
|
||||
&worker_name,
|
||||
&job_completed_tx,
|
||||
)
|
||||
.await;
|
||||
workers.into_iter().for_each(|(path, sender, handle)| {
|
||||
dedicated_handles.push(handle);
|
||||
hm.insert(path, sender);
|
||||
});
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tracing::error!(
|
||||
"flow present but value not found for {}, waiting 10s",
|
||||
flow_path
|
||||
);
|
||||
}
|
||||
} else {
|
||||
tracing::error!(
|
||||
"flow present but value not found for dedicated worker. {}",
|
||||
flow_path
|
||||
);
|
||||
tracing::error!("flow not found for {}, waiting 10s,", flow_path);
|
||||
}
|
||||
} else {
|
||||
tracing::error!("flow not found for dedicated worker: {}. Waiting for dependency job and expected to restart.", flow_path);
|
||||
tokio::time::sleep(Duration::from_millis(10000)).await;
|
||||
}
|
||||
} else {
|
||||
is_flow_worker = false;
|
||||
if let Some((path, sender, handle)) = spawn_dedicated_worker(
|
||||
SpawnWorker::Script { path: _wp.path.clone(), hash: None },
|
||||
&_wp.workspace_id,
|
||||
killpill_tx.clone(),
|
||||
&killpill_rx,
|
||||
db,
|
||||
&worker_dir,
|
||||
base_internal_url,
|
||||
&worker_name,
|
||||
&job_completed_tx,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
dedicated_handles.push(handle);
|
||||
hm.insert(path, sender);
|
||||
} else {
|
||||
tracing::error!(
|
||||
"failed to spawn dedicated worker for {}, script not found",
|
||||
loop {
|
||||
if let Some((path, sender, handle)) = spawn_dedicated_worker(
|
||||
SpawnWorker::Script { path: _wp.path.clone(), hash: None },
|
||||
&_wp.workspace_id,
|
||||
killpill_tx.clone(),
|
||||
&killpill_rx,
|
||||
db,
|
||||
&worker_dir,
|
||||
base_internal_url,
|
||||
&worker_name,
|
||||
&job_completed_tx,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
dedicated_handles.push(handle);
|
||||
hm.insert(path, sender);
|
||||
break;
|
||||
} else {
|
||||
tracing::error!(
|
||||
"failed to spawn dedicated worker for {}, script found but not in a compatible language. Retrying 10s",
|
||||
_wp.path
|
||||
);
|
||||
tokio::time::sleep(Duration::from_millis(10000)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
(hm, is_flow_worker)
|
||||
@@ -1845,7 +1851,7 @@ async fn spawn_dedicated_worker(
|
||||
{
|
||||
let (dedicated_worker_tx, dedicated_worker_rx) =
|
||||
mpsc::channel::<Arc<QueuedJob>>(MAX_BUFFERED_DEDICATED_JOBS);
|
||||
let killpill_rx = killpill_rx.resubscribe();
|
||||
let mut killpill_rx = killpill_rx.resubscribe();
|
||||
let db = db.clone();
|
||||
let base_internal_url = base_internal_url.to_string();
|
||||
let worker_name = worker_name.to_string();
|
||||
@@ -1864,12 +1870,16 @@ async fn spawn_dedicated_worker(
|
||||
|
||||
let (content, lock, language, envs) = match sw {
|
||||
SpawnWorker::Script { path, hash } => {
|
||||
let q = if let Some(hash) = hash {
|
||||
get_script_content_by_hash(&hash, &w_id, &db).await.map(
|
||||
|r: ContentReqLangEnvs| Some((r.content, r.lockfile, r.language, r.envs)),
|
||||
)
|
||||
} else {
|
||||
sqlx::query_as::<_, (String, Option<String>, Option<ScriptLang>, Option<Vec<String>>)>(
|
||||
let r;
|
||||
loop {
|
||||
let q = if let Some(hash) = hash {
|
||||
get_script_content_by_hash(&hash, &w_id, &db).await.map(
|
||||
|r: ContentReqLangEnvs| {
|
||||
Some((r.content, r.lockfile, r.language, r.envs))
|
||||
},
|
||||
)
|
||||
} else {
|
||||
sqlx::query_as::<_, (String, Option<String>, Option<ScriptLang>, Option<Vec<String>>)>(
|
||||
"SELECT content, lock, language, envs FROM script WHERE path = $1 AND workspace_id = $2 AND
|
||||
created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2 AND
|
||||
deleted = false AND lock IS not NULL AND lock_error_logs IS NULL)",
|
||||
@@ -1879,23 +1889,37 @@ async fn spawn_dedicated_worker(
|
||||
.fetch_optional(&db)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("expected content and lock: {e}")))
|
||||
};
|
||||
if let Ok(q) = q {
|
||||
if let Some(wp) = q {
|
||||
wp
|
||||
};
|
||||
|
||||
if let Ok(q) = q {
|
||||
if let Some(wp) = q {
|
||||
r = wp;
|
||||
break;
|
||||
} else {
|
||||
tracing::error!(
|
||||
"Failed to fetch script `{}` in workspace {} for dedicated worker. Retrying in 10s.",
|
||||
path,
|
||||
w_id
|
||||
);
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = killpill_rx.recv() => {
|
||||
tracing::info!("Killing dedicated worker while it was attempting to fetch script");
|
||||
return None;
|
||||
}
|
||||
_ = tokio::time::sleep(Duration::from_secs(10)) => {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tracing::error!(
|
||||
"Failed to fetch script `{}` in workspace {} for dedicated worker.",
|
||||
path,
|
||||
w_id
|
||||
);
|
||||
tracing::error!("Failed to fetch script for dedicated worker");
|
||||
killpill_tx.send(()).expect("send");
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Failed to fetch script for dedicated worker");
|
||||
killpill_tx.send(()).expect("send");
|
||||
return None;
|
||||
}
|
||||
r
|
||||
}
|
||||
SpawnWorker::RawScript { content, lock, lang, .. } => (content, lock, Some(lang), None),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
|
||||
import { JSDOM } from "https://jspm.dev/jsdom@22";
|
||||
import { JSDOM } from "https://jspm.dev/jsdom";
|
||||
|
||||
type DataPoint = {
|
||||
value: number;
|
||||
|
||||
@@ -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.217.0";
|
||||
export const VERSION = "v1.216.0";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -31,7 +31,7 @@ addEventListener("error", (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
export const VERSION = "v1.217.0";
|
||||
export const VERSION = "v1.216.0";
|
||||
|
||||
let command: any = new Command()
|
||||
.name("wmill")
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{$BASE_URL} {
|
||||
bind {$ADDRESS}
|
||||
tls /certs/caddy.crt /certs/caddy.key
|
||||
handle_path /static/* {
|
||||
root * /static/
|
||||
file_server {
|
||||
browse
|
||||
}
|
||||
}
|
||||
handle_path /npm/* {
|
||||
reverse_proxy * http://npm_registry:4873
|
||||
}
|
||||
handle_path /* {
|
||||
reverse_proxy * http://pypi_server:8080
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
Private package registry with self-signed certificates
|
||||
==================================================
|
||||
|
||||
Setup a private NPM registry and Pypi server behind a revers proxy (Caddy) exposing an HTTPS endpoint with self signed certificates
|
||||
|
||||
### Setup
|
||||
|
||||
1. Generate self signed certificates (or bring your own)
|
||||
```bash
|
||||
cd certs
|
||||
# feel free to read the file anc change the
|
||||
./generate_certs.sh
|
||||
# choose a password for the RootCA key. You'll need to input it for pretty much all following steps
|
||||
```
|
||||
At the end of the script, you should have multiple files in the `certs/` folder. The most important ones are:
|
||||
- `windmill-root.key` (Root CA private key)
|
||||
- `windmill-root.crt` (Root CA certificate)
|
||||
- `caddy.key` (caddy server private key)
|
||||
- `caddy.crt` (caddy registry server certificate)
|
||||
|
||||
2. Start the docker compose stack
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This will start the private NPM registry, the private Pypi server and Caddy as a reverse proxy sitting in front of the two. Separately, for the purpose of proving an end to end setup, it will also start minimal Windmill stack composed of just one Windmill server/worker and the associated database.
|
||||
|
||||
For a more complete Windmill setup, we invite you to refer to the latest [docker compose](/docker-compose.yml) at the root of this repository to setup a more evolved Windmill stack.
|
||||
|
||||
For the private registries/repositories, it's using [Verdaccio](https://verdaccio.org/) as an easy-to-deploy NPM registry and [pypiserver](https://pypi.org/project/pypiserver/) for Python.
|
||||
|
||||
## Deno pulling package from private NPM registry
|
||||
|
||||
Upload the custom `helloworld_npm_package` package to the private NPM registry and use it in a Windmill script
|
||||
|
||||
```bash
|
||||
cd helloworld_npm_package
|
||||
# you need to first create a registry user
|
||||
# you might need to run `npm config set strict-ssl false` if the below fails. If you do, then change it back to `true` after running the 2 commands
|
||||
npm adduser --registry https://localhost/npm/
|
||||
npm publish --registry https://localhost/npm/
|
||||
```
|
||||
|
||||
Go to Windmill at `http://localhost:8000`. Create a simple Deno script:
|
||||
```ts
|
||||
import * as testpackage from "npm:@windmill/helloworld@0.0.1"
|
||||
|
||||
export async function main() {
|
||||
console.log(testpackage.sayHello("Windmill"))
|
||||
}
|
||||
```
|
||||
and execute it. It should return successfully with:
|
||||
```
|
||||
Hello Windmill
|
||||
```
|
||||
|
||||
Note: Native fetches to HTTPS endpoints with self signed certificates also takes into account the DENO_CERT environment variable
|
||||
|
||||
Go to Windmill and create a new script of type "REST" with the following content:
|
||||
```ts
|
||||
export async function main() {
|
||||
const res = await fetch("https://caddy/static/helloworld.json", {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
```
|
||||
|
||||
It will fetch a static json file from Caddy exposed behind its HTTPS endpoint with self signed certificate.
|
||||
|
||||
## Python pulling package from private NPM registry
|
||||
|
||||
Upload the custom `helloworld_python_module` python module to the private Pypi server
|
||||
|
||||
```bash
|
||||
cd helloworld_python_module
|
||||
python setup.py sdist
|
||||
# using twine to upload the module here, get it with `pip install twine`
|
||||
twine upload --repository-url https://localhost/ dist/* --cert ../certs/windmill-root.crt
|
||||
# no username and password, just press enter. For the purpose of the demo we're running pypiserver completely unauthenticated
|
||||
```
|
||||
You can check that the package is uploaded by visiting [https://localhost/simple](https://localhost/simple).
|
||||
|
||||
Go to Windmill at `http://localhost:8000`. Create a simple Python script:
|
||||
```python
|
||||
#requirements:
|
||||
#windmill-helloworld==0.0.1
|
||||
import windmill_helloworld
|
||||
|
||||
def main():
|
||||
print(windmill_helloworld.say_hello("Windmill"))
|
||||
```
|
||||
and execute it. It should return successfully with:
|
||||
```
|
||||
Hello Windmill
|
||||
```
|
||||
|
||||
### MISC
|
||||
|
||||
1. `DENO_TLS_CA_STORE` VS `DENO_CERT`
|
||||
Both works. `DENO_CERT` is better b/c you just have to set it to the path of the trusted Root CA certificate, and deno will trust this certificate.
|
||||
When using `DENO_TLS_CA_STORE=system`, you _have to_ make the server trust the custom Root CA certificate with the following commands:
|
||||
```bash
|
||||
# in the windmill-server container:
|
||||
cp /custom-certs/windmill-root.crt /usr/local/share/ca-certificates/
|
||||
update-ca-certificates # as root
|
||||
# the output should tell (among other things): " ... 1 added, 0 removed; done. ..."
|
||||
```
|
||||
|
||||
2. Running Deno scripts manually in the Windmill container
|
||||
This could be useful for debugging purposes.
|
||||
|
||||
```bash
|
||||
# log into the Windmill Server container
|
||||
docker exec -it <WINDMILL_SERVER_CONTAINER> /bin/bash
|
||||
|
||||
# Go into Deno REPL
|
||||
deno
|
||||
|
||||
# try to import the package
|
||||
import * as testpackage from "npm:@windmill/helloworld@0.0.1"
|
||||
> undefined
|
||||
|
||||
# if the above returns undefined, there's a good chance it's working. If you want to double check:
|
||||
testpackage.sayHello("Windmill")
|
||||
> Hello Windmill
|
||||
|
||||
# potentially inspect the env var available to DENO. Is you used DENO_CERT in the docker compose, check its value:
|
||||
console.log(Deno.env.get("DENO_CERT"))
|
||||
> /custom-certs/windmill-root.crt
|
||||
```
|
||||
|
||||
3. Manually testing Pypi private server integration
|
||||
Can be useful to debug as well:
|
||||
|
||||
```bash
|
||||
# install the package
|
||||
pip3 install --cert ../certs/windmill-root.crt -i https://localhost/simple/ windmill-helloworld
|
||||
|
||||
# go to python CLI and try to import it and use it
|
||||
python
|
||||
>>> import windmill_helloworld
|
||||
>>> windmill_helloworld.say_hello("world")
|
||||
'Hello world'
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
*.crt
|
||||
*.csr
|
||||
*.key
|
||||
*.ext
|
||||
*.srl
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
set eou -pipefail
|
||||
|
||||
CANAME='windmill-root'
|
||||
|
||||
COUNTRY='FR'
|
||||
STATE='Paris'
|
||||
CITY='Paris'
|
||||
ORGANIZATION='WindmillLabs'
|
||||
ROOT_CA_CN='WindmillRootCA'
|
||||
SERVER_CA_CN='caddy' # IMPORTANT: set this to the FQDN of the caddy server. Here in the docker compose stack, it will be caddy
|
||||
|
||||
echo "Generating RootCA key"
|
||||
openssl genrsa -aes256 -out $CANAME.key 4096
|
||||
echo "Generating RootCA certificate"
|
||||
openssl req -x509 -new -nodes -key $CANAME.key -sha256 -days 1826 -out $CANAME.crt -subj "/CN=${ROOT_CA_CN}/C=${COUNTRY}/ST=${STATE}/L=${CITY}/O=${ORGANIZATION}"
|
||||
|
||||
CERTNAME=caddy
|
||||
echo "Generating server certificate private key and cert signing request"
|
||||
openssl req -new -nodes -out $CERTNAME.csr -newkey rsa:4096 -keyout $CERTNAME.key -subj "/CN=${SERVER_CA_CN}/C=${COUNTRY}/ST=${STATE}/L=${CITY}/O=${ORGANIZATION}"
|
||||
|
||||
echo "Generating server certificate"
|
||||
cat > $CERTNAME.v3.ext << EOF
|
||||
[v3_req]
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
DNS.1 = caddy
|
||||
DNS.2 = localhost
|
||||
EOF
|
||||
# ^ HERE ^ in the above alt_names, feel free to add any alternate CN
|
||||
|
||||
openssl x509 -req -in $CERTNAME.csr -CA $CANAME.crt -CAkey $CANAME.key -CAcreateserial -out $CERTNAME.crt -days 1826 -sha256 -extensions v3_req -extfile $CERTNAME.v3.ext
|
||||
@@ -1,69 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
npm_registry:
|
||||
image: verdaccio/verdaccio
|
||||
environment:
|
||||
- VERDACCIO_PROTOCOL=http
|
||||
- VERDACCIO_PUBLIC_URL=http://caddy/npm/
|
||||
volumes:
|
||||
- ./verdaccio_conf:/verdaccio/conf
|
||||
- npm_registry_data:/verdaccio/storage
|
||||
|
||||
pypi_server:
|
||||
image: pypiserver/pypiserver:latest
|
||||
platform: linux/x86_64
|
||||
volumes:
|
||||
- pypi_data:/data/packages
|
||||
command: run -P . -a . /data/packages
|
||||
|
||||
caddy:
|
||||
image: caddy:2.5.2-alpine
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
- ./certs:/certs
|
||||
- ./helloworld_static:/static/
|
||||
ports:
|
||||
- 443:443
|
||||
environment:
|
||||
- BASE_URL=":443"
|
||||
|
||||
db:
|
||||
image: postgres:14
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: changeme
|
||||
POSTGRES_DB: windmill
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
windmill_server:
|
||||
image: ghcr.io/windmill-labs/windmill:main
|
||||
ports:
|
||||
- 8000:8000
|
||||
environment:
|
||||
- DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
|
||||
- WORKER_TAGS=deno,go,python3,bash,flow,hub,dependency,nativets
|
||||
|
||||
# For DENO and REST scripts:
|
||||
- NPM_CONFIG_REGISTRY=http://caddy/npm/
|
||||
- DENO_CERT=/custom-certs/windmill-root.crt # this will make deno trust this RootCA for all sessions
|
||||
# - DENO_TLS_CA_STORE=system # alternatively, you can use this but you'll need to manually trust the RootCA at the host level, see README.md
|
||||
|
||||
# For Python scripts:
|
||||
- PIP_INDEX_URL=https://caddy/simple/
|
||||
- PIP_INDEX_CERT=/custom-certs/windmill-root.crt # this will make pip trust this RootCA for all sessions
|
||||
volumes:
|
||||
- ./certs:/custom-certs
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
npm_registry_data: null
|
||||
pypi_data: null
|
||||
db_data: null
|
||||
@@ -1 +0,0 @@
|
||||
node_modules/
|
||||
@@ -1,4 +0,0 @@
|
||||
Hello world test package
|
||||
========================
|
||||
|
||||
Dummy NPM package to test imports from a private NPM registry in Windmill
|
||||
@@ -1,3 +0,0 @@
|
||||
exports.sayHello = function(x) {
|
||||
return `Hello ${x}`
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "@windmill/helloworld",
|
||||
"version": "0.0.1",
|
||||
"description": "hello world test package",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
"keywords": [],
|
||||
"author": "Windmill",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
dist/
|
||||
__pycache__/
|
||||
*.egg-info/
|
||||
@@ -1,4 +0,0 @@
|
||||
Hello world test module
|
||||
=======================
|
||||
|
||||
Dummy Python module to test imports from a private Pypi server in Windmill
|
||||
@@ -1,30 +0,0 @@
|
||||
import os
|
||||
|
||||
import setuptools
|
||||
|
||||
module_path = os.path.join(os.path.dirname(__file__), "windmill_helloworld.py")
|
||||
|
||||
setuptools.setup(
|
||||
name="windmill-helloworld",
|
||||
version="0.0.1",
|
||||
url="https://github.com/windmill-labs/windmill/blob//examples/deploy/private-package-registry-tls/README.md",
|
||||
author="WindmillLabs",
|
||||
author_email="contact@windmill.dev",
|
||||
description="Simple hello world python module to host on a private Pypi server",
|
||||
long_description=open("README.md").read(),
|
||||
py_modules=["windmill_helloworld"],
|
||||
zip_safe=False,
|
||||
platforms="any",
|
||||
install_requires=[],
|
||||
classifiers=[
|
||||
"Development Status :: 2 - Pre-Alpha",
|
||||
"Environment :: Web Environment",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
],
|
||||
)
|
||||
@@ -1,2 +0,0 @@
|
||||
def say_hello(x: str) -> str:
|
||||
return "Hello {}".format(x)
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"hello": "world"
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
#
|
||||
# This is the default configuration file. It allows all users to do anything,
|
||||
# please read carefully the documentation and best practices to
|
||||
# improve security.
|
||||
#
|
||||
# Do not configure host and port under `listen` in this file
|
||||
# as it will be ignored when using docker.
|
||||
# see https://verdaccio.org/docs/en/docker#docker-and-custom-port-configuration
|
||||
#
|
||||
# Look here for more config file examples:
|
||||
# https://github.com/verdaccio/verdaccio/tree/5.x/conf
|
||||
#
|
||||
# Read about the best practices
|
||||
# https://verdaccio.org/docs/best
|
||||
|
||||
# path to a directory with all packages
|
||||
storage: /verdaccio/storage/data
|
||||
# path to a directory with plugins to include
|
||||
plugins: /verdaccio/plugins
|
||||
|
||||
# https://verdaccio.org/docs/webui
|
||||
web:
|
||||
title: Verdaccio
|
||||
# comment out to disable gravatar support
|
||||
# gravatar: false
|
||||
# by default packages are ordercer ascendant (asc|desc)
|
||||
# sort_packages: asc
|
||||
# convert your UI to the dark side
|
||||
# darkMode: true
|
||||
# html_cache: true
|
||||
# by default all features are displayed
|
||||
# login: true
|
||||
# showInfo: true
|
||||
# showSettings: true
|
||||
# In combination with darkMode you can force specific theme
|
||||
# showThemeSwitch: true
|
||||
# showFooter: true
|
||||
# showSearch: true
|
||||
# showRaw: true
|
||||
# showDownloadTarball: true
|
||||
# HTML tags injected after manifest <scripts/>
|
||||
# scriptsBodyAfter:
|
||||
# - '<script type="text/javascript" src="https://my.company.com/customJS.min.js"></script>'
|
||||
# HTML tags injected before ends </head>
|
||||
# metaScripts:
|
||||
# - '<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>'
|
||||
# - '<script type="text/javascript" src="https://browser.sentry-cdn.com/5.15.5/bundle.min.js"></script>'
|
||||
# - '<meta name="robots" content="noindex" />'
|
||||
# HTML tags injected first child at <body/>
|
||||
# bodyBefore:
|
||||
# - '<div id="myId">html before webpack scripts</div>'
|
||||
# Public path for template manifest scripts (only manifest)
|
||||
# publicPath: http://somedomain.org/
|
||||
|
||||
# https://verdaccio.org/docs/configuration#authentication
|
||||
auth:
|
||||
htpasswd:
|
||||
file: /verdaccio/storage/htpasswd
|
||||
# Maximum amount of users allowed to register, defaults to "+infinity".
|
||||
# You can set this to -1 to disable registration.
|
||||
# max_users: 1000
|
||||
# Hash algorithm, possible options are: "bcrypt", "md5", "sha1", "crypt".
|
||||
# algorithm: bcrypt # by default is crypt, but is recommended use bcrypt for new installations
|
||||
# Rounds number for "bcrypt", will be ignored for other algorithms.
|
||||
# rounds: 10
|
||||
|
||||
# https://verdaccio.org/docs/configuration#uplinks
|
||||
# a list of other known repositories we can talk to
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
|
||||
# Learn how to protect your packages
|
||||
# https://verdaccio.org/docs/protect-your-dependencies/
|
||||
# https://verdaccio.org/docs/configuration#packages
|
||||
packages:
|
||||
'@*/*':
|
||||
# scoped packages
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
unpublish: $authenticated
|
||||
proxy: npmjs
|
||||
|
||||
'**':
|
||||
# allow all users (including non-authenticated users) to read and
|
||||
# publish all packages
|
||||
#
|
||||
# you can specify usernames/groupnames (depending on your auth plugin)
|
||||
# and three keywords: "$all", "$anonymous", "$authenticated"
|
||||
access: $all
|
||||
|
||||
# allow all known users to publish/publish packages
|
||||
# (anyone can register by default, remember?)
|
||||
publish: $authenticated
|
||||
unpublish: $authenticated
|
||||
|
||||
# if package is not available locally, proxy requests to 'npmjs' registry
|
||||
proxy: npmjs
|
||||
|
||||
# To improve your security configuration and avoid dependency confusion
|
||||
# consider removing the proxy property for private packages
|
||||
# https://verdaccio.org/docs/best#remove-proxy-to-increase-security-at-private-packages
|
||||
|
||||
# https://verdaccio.org/docs/configuration#server
|
||||
# You can specify HTTP/1.1 server keep alive timeout in seconds for incoming connections.
|
||||
# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout.
|
||||
# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enough.
|
||||
server:
|
||||
keepAliveTimeout: 60
|
||||
# Allow `req.ip` to resolve properly when Verdaccio is behind a proxy or load-balancer
|
||||
# See: https://expressjs.com/en/guide/behind-proxies.html
|
||||
# trustProxy: '127.0.0.1'
|
||||
|
||||
# https://verdaccio.org/docs/configuration#offline-publish
|
||||
# publish:
|
||||
# allow_offline: false
|
||||
|
||||
# https://verdaccio.org/docs/configuration#url-prefix
|
||||
# url_prefix: /verdaccio/
|
||||
# VERDACCIO_PUBLIC_URL='https://somedomain.org';
|
||||
# url_prefix: '/my_prefix'
|
||||
# // url -> https://somedomain.org/my_prefix/
|
||||
# VERDACCIO_PUBLIC_URL='https://somedomain.org';
|
||||
# url_prefix: '/'
|
||||
# // url -> https://somedomain.org/
|
||||
# VERDACCIO_PUBLIC_URL='https://somedomain.org/first_prefix';
|
||||
# url_prefix: '/second_prefix'
|
||||
# // url -> https://somedomain.org/second_prefix/'
|
||||
|
||||
# https://verdaccio.org/docs/configuration#security
|
||||
# security:
|
||||
# api:
|
||||
# legacy: true
|
||||
# jwt:
|
||||
# sign:
|
||||
# expiresIn: 29d
|
||||
# verify:
|
||||
# someProp: [value]
|
||||
# web:
|
||||
# sign:
|
||||
# expiresIn: 1h # 1 hour by default
|
||||
# verify:
|
||||
# someProp: [value]
|
||||
|
||||
# https://verdaccio.org/docs/configuration#user-rate-limit
|
||||
# userRateLimit:
|
||||
# windowMs: 50000
|
||||
# max: 1000
|
||||
|
||||
# https://verdaccio.org/docs/configuration#max-body-size
|
||||
# max_body_size: 10mb
|
||||
|
||||
# https://verdaccio.org/docs/configuration#listen-port
|
||||
# listen:
|
||||
# - localhost:4873 # default value
|
||||
# - http://localhost:4873 # same thing
|
||||
# - 0.0.0.0:4873 # listen on all addresses (INADDR_ANY)
|
||||
# - https://example.org:4873 # if you want to use https
|
||||
# - "[::1]:4873" # ipv6
|
||||
# - unix:/tmp/verdaccio.sock # unix socket
|
||||
|
||||
# The HTTPS configuration is useful if you do not consider use a HTTP Proxy
|
||||
# https://verdaccio.org/docs/configuration#https
|
||||
# https:
|
||||
# key: ./path/verdaccio-key.pem
|
||||
# cert: ./path/verdaccio-cert.pem
|
||||
# ca: ./path/verdaccio-csr.pem
|
||||
|
||||
# https://verdaccio.org/docs/configuration#proxy
|
||||
# http_proxy: http://something.local/
|
||||
# https_proxy: https://something.local/
|
||||
|
||||
# https://verdaccio.org/docs/configuration#notifications
|
||||
# notify:
|
||||
# method: POST
|
||||
# headers: [{ "Content-Type": "application/json" }]
|
||||
# endpoint: https://usagge.hipchat.com/v2/room/3729485/notification?auth_token=mySecretToken
|
||||
# content: '{"color":"green","message":"New package published: * {{ name }}*","notify":true,"message_format":"text"}'
|
||||
|
||||
middlewares:
|
||||
audit:
|
||||
enabled: true
|
||||
|
||||
# https://verdaccio.org/docs/logger
|
||||
# log settings
|
||||
log: { type: stdout, format: pretty, level: http }
|
||||
#experiments:
|
||||
# # support for npm token command
|
||||
# token: false
|
||||
# # enable tarball URL redirect for hosting tarball with a different server, the tarball_url_redirect can be a template string
|
||||
# tarball_url_redirect: 'https://mycdn.com/verdaccio/${packageName}/${filename}'
|
||||
# # the tarball_url_redirect can be a function, takes packageName and filename and returns the url, when working with a js configuration file
|
||||
# tarball_url_redirect(packageName, filename) {
|
||||
# const signedUrl = // generate a signed url
|
||||
# return signedUrl;
|
||||
# }
|
||||
|
||||
# translate your registry, api i18n not available yet
|
||||
# i18n:
|
||||
# list of the available translations https://github.com/verdaccio/verdaccio/blob/master/packages/plugins/ui-theme/src/i18n/ABOUT_TRANSLATIONS.md
|
||||
# web: en-US
|
||||
@@ -1,20 +0,0 @@
|
||||
storage: /verdaccio/storage/data
|
||||
plugins: /verdaccio/plugins
|
||||
auth:
|
||||
htpasswd:
|
||||
file: /verdaccio/storage/htpasswd
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
packages:
|
||||
"@*/*":
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
"**":
|
||||
access: $all
|
||||
proxy: npmjs
|
||||
listen:
|
||||
- http://localhost:4873
|
||||
- http://npm_registry:4873
|
||||
url_prefix: /npm/
|
||||
log: { type: stdout, format: pretty, level: http }
|
||||
@@ -1,24 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import tailwindClasses from './tailwindClasses.js'
|
||||
|
||||
function filterTailwindClasses(classes) {
|
||||
const filters = [
|
||||
{ pattern: /^m(\w?)-.*$/ },
|
||||
{ pattern: /^p(\w?)-.*$/ },
|
||||
{ pattern: /^rounded-.*$/ },
|
||||
{ pattern: /^shadow-.*$/ },
|
||||
{ pattern: /^text-[^/]*$/ },
|
||||
{ pattern: /^bg-[^/]*$/ },
|
||||
{ pattern: /^border-[^/]*$/ },
|
||||
{ pattern: /^ring-[^/]*$/ }
|
||||
]
|
||||
|
||||
return classes.filter((className) => {
|
||||
return filters.some((filter) => filter.pattern.test(className))
|
||||
})
|
||||
}
|
||||
|
||||
const filteredClasses = filterTailwindClasses(tailwindClasses)
|
||||
|
||||
const output = `export const filteredTailwindClasses = ${JSON.stringify(filteredClasses, null, 2)};`
|
||||
fs.writeFileSync('filteredTailwindClasses.ts', output)
|
||||
12
frontend/package-lock.json
generated
12
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.217.0",
|
||||
"version": "1.216.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.217.0",
|
||||
"version": "1.216.0",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^4.0.0",
|
||||
@@ -30,7 +30,7 @@
|
||||
"graphql": "^16.7.1",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-svelte": "^0.293.0",
|
||||
"lucide-svelte": "^0.277.0",
|
||||
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
|
||||
"monaco-graphql": "^1.3.0",
|
||||
"monaco-languageclient": "~7.0.1",
|
||||
@@ -4982,9 +4982,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/lucide-svelte": {
|
||||
"version": "0.293.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.293.0.tgz",
|
||||
"integrity": "sha512-nQ6QO6anjjiVoipw3dNptzI6NWuiArlhysVtRpoAWICSQVMR3ybwVf6piUUqbjGe+RfGufLzCl93C/yhsS+p4w==",
|
||||
"version": "0.277.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.277.0.tgz",
|
||||
"integrity": "sha512-HrlMk0mTOd3MVSWA2ZHR79QrCycLRV4pey4HWGnGGoVFsh0/P7zGyDCJxy/MKe2t4SJtcStLdKIybMHc5pjFSQ==",
|
||||
"peerDependencies": {
|
||||
"svelte": ">=3 <5"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.217.0",
|
||||
"version": "1.216.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -14,7 +14,7 @@
|
||||
"generate-backend-client-mac": "openapi --input ../backend/windmill-api/openapi.yaml --output ./src/lib/gen --useOptions",
|
||||
"pretest": "tsc --incremental -p tests/tsconfig.json",
|
||||
"test": "playwright test --config=tests-out/playwright.config.js",
|
||||
"filter-classes": "node filterTailwindClasses.js"
|
||||
"delele-orphan-components": "sh scripts/delete-orphan-components.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@floating-ui/core": "^1.3.1",
|
||||
@@ -113,7 +113,7 @@
|
||||
"graphql": "^16.7.1",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-svelte": "^0.293.0",
|
||||
"lucide-svelte": "^0.277.0",
|
||||
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
|
||||
"monaco-graphql": "^1.3.0",
|
||||
"monaco-languageclient": "~7.0.1",
|
||||
@@ -402,4 +402,4 @@
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
72
frontend/scripts/delete-orphan-components.sh
Normal file
72
frontend/scripts/delete-orphan-components.sh
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ANSI color codes
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# get the list of all .svelte files
|
||||
svelte_files=$(find src -type f -name "*.svelte")
|
||||
|
||||
# Array to hold unused files
|
||||
declare -a unused_files
|
||||
|
||||
echo -e "${NC}Scanning src folder to find all .svelte files"
|
||||
echo -e " ${GREEN}.${NC} means the .svelte is imported in another file"
|
||||
echo -e " ${RED}x${NC} means the .svelte is not imported and should likely be removed"
|
||||
|
||||
# loop over each svelte file
|
||||
for svelte_file in $svelte_files
|
||||
do
|
||||
# extract the filename
|
||||
filename=$(basename -- "$svelte_file")
|
||||
|
||||
# skip files starting with '+'
|
||||
if [[ "$filename" == +* ]]
|
||||
then
|
||||
echo -n -e "${GREEN}.${NC}"
|
||||
continue
|
||||
fi
|
||||
|
||||
# search for the filename in all files
|
||||
found=$(grep -rl "$filename" src)
|
||||
|
||||
# if nothing was found, then the file is unused
|
||||
if [[ -z $found ]]
|
||||
then
|
||||
echo -n -e "${RED}x${NC}"
|
||||
unused_files+=("$svelte_file")
|
||||
else
|
||||
echo -n -e "${GREEN}.${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Print a newline after progress dots
|
||||
echo
|
||||
echo
|
||||
|
||||
# Print unused files
|
||||
for file in "${unused_files[@]}"
|
||||
do
|
||||
echo -e "${RED}Unused Svelte file: $file${NC}"
|
||||
done
|
||||
|
||||
# If no unused components found, print the message and exit
|
||||
if [ ${#unused_files[@]} -eq 0 ]; then
|
||||
echo -e "${GREEN}No unused components found.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Delete files if user confirms
|
||||
if [ ${#unused_files[@]} -gt 0 ]; then
|
||||
echo -e -n "${GREEN}Do you want to delete these ${#unused_files[@]} files? (y/n) ${NC}"
|
||||
read answer
|
||||
|
||||
if [ "$answer" != "${answer#[Yy]}" ] ;then
|
||||
for file in "${unused_files[@]}"
|
||||
do
|
||||
rm "$file"
|
||||
echo -e "${RED}Deleted $file${NC}"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
@@ -301,17 +301,11 @@
|
||||
} gap-2 h-full items-center`}
|
||||
>
|
||||
{#if file_info.type === 'folder'}
|
||||
{#if file_info.collapsed}<FolderClosed size={16} />{:else}<FolderOpen
|
||||
size={16}
|
||||
/>{/if}
|
||||
<div class="truncate text-ellipsis w-56">
|
||||
{file_info.display_name}
|
||||
</div>
|
||||
{#if file_info.collapsed}<FolderClosed />{:else}<FolderOpen />{/if}
|
||||
{file_info.display_name}
|
||||
{:else}
|
||||
<File size={16} />
|
||||
<div class="truncate text-ellipsis w-56">
|
||||
{file_info.display_name}
|
||||
</div>
|
||||
<File />
|
||||
{file_info.display_name}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -372,7 +366,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<pre class="grow whitespace-no-wrap break-words"
|
||||
>{#if !emptyString(filePreview.contentPreview)}{filePreview.contentPreview}{:else if filePreview.contentType !== undefined}Preview impossible.{/if}</pre
|
||||
>{#if !emptyString(filePreview.contentPreview)}{filePreview.contentPreview}{:else if filePreview.contentType !== undefined}Preview impossible. If it's a CSV file, you can try changing the separator{/if}</pre
|
||||
>
|
||||
{:else if filePreviewLoading}
|
||||
<div class="flex h-6 items-center text-tertiary mb-4">
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
<script context="module">
|
||||
let cssClassesLoaded = writable(false)
|
||||
let tailwindClassesLoaded = writable(false)
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { BROWSER } from 'esm-env'
|
||||
|
||||
@@ -25,7 +20,7 @@
|
||||
import 'monaco-editor/esm/vs/basic-languages/css/css.contribution'
|
||||
import 'monaco-editor/esm/vs/language/css/monaco.contribution'
|
||||
|
||||
import { allClasses } from './apps/editor/componentsPanel/cssUtils'
|
||||
import { allClasses, authorizedClassnames } from './apps/editor/componentsPanel/cssUtils'
|
||||
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
|
||||
@@ -33,8 +28,6 @@
|
||||
import { buildWorkerDefinition } from './build_workers'
|
||||
import { initializeVscode } from './vscode'
|
||||
import EditorTheme from './EditorTheme.svelte'
|
||||
import { tailwindClasses } from './apps/editor/componentsPanel/tailwindUtils'
|
||||
import { writable } from 'svelte/store'
|
||||
// import { createConfiguredEditor } from 'vscode/monaco'
|
||||
// import type { IStandaloneCodeEditor } from 'vscode/vscode/vs/editor/standalone/browser/standaloneCodeEditor'
|
||||
|
||||
@@ -123,7 +116,6 @@
|
||||
|
||||
let width = 0
|
||||
let initialized = false
|
||||
|
||||
async function loadMonaco() {
|
||||
await initializeVscode()
|
||||
initialized = true
|
||||
@@ -210,19 +202,10 @@
|
||||
editor.onDidBlurEditorText(() => {
|
||||
code = getCode()
|
||||
})
|
||||
|
||||
if (lang === 'css' && !$cssClassesLoaded) {
|
||||
$cssClassesLoaded = true
|
||||
addCSSClassCompletions()
|
||||
}
|
||||
|
||||
if (lang === 'tailwindcss' && !$tailwindClassesLoaded) {
|
||||
languages.register({ id: 'tailwindcss' })
|
||||
$tailwindClassesLoaded = true
|
||||
addTailwindClassCompletions()
|
||||
}
|
||||
}
|
||||
|
||||
$: lang == 'css' && initialized && addCSSClassCompletions()
|
||||
|
||||
function addCSSClassCompletions() {
|
||||
languages.registerCompletionItemProvider('css', {
|
||||
provideCompletionItems: function (model, position, context, token) {
|
||||
@@ -255,38 +238,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
function addTailwindClassCompletions() {
|
||||
languages.registerCompletionItemProvider('tailwindcss', {
|
||||
provideCompletionItems: function (model, position, context, token) {
|
||||
const word = model.getWordUntilPosition(position)
|
||||
const range = {
|
||||
startLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endLineNumber: position.lineNumber,
|
||||
endColumn: word.endColumn
|
||||
}
|
||||
|
||||
if (word && word.word) {
|
||||
const currentWord = word.word
|
||||
|
||||
const suggestions = tailwindClasses
|
||||
.filter((className) => className.includes(currentWord))
|
||||
.map((className) => ({
|
||||
label: className,
|
||||
kind: languages.CompletionItemKind.Class,
|
||||
insertText: className,
|
||||
documentation: 'Custom CSS class',
|
||||
range: range
|
||||
}))
|
||||
|
||||
return { suggestions }
|
||||
}
|
||||
|
||||
return { suggestions: [] }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function loadExtraLib() {
|
||||
if (lang == 'javascript') {
|
||||
const stdLib = { content: libStdContent, filePath: 'es5.d.ts' }
|
||||
@@ -301,6 +252,38 @@
|
||||
} else {
|
||||
languages.typescript.javascriptDefaults.setExtraLibs([stdLib])
|
||||
}
|
||||
} else if (lang === 'css') {
|
||||
const cssClasses = authorizedClassnames.map((className) => '.' + className)
|
||||
|
||||
languages.registerCompletionItemProvider('css', {
|
||||
provideCompletionItems: function (model, position, context, token) {
|
||||
const word = model.getWordUntilPosition(position)
|
||||
const range = {
|
||||
startLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endLineNumber: position.lineNumber,
|
||||
endColumn: word.endColumn
|
||||
}
|
||||
|
||||
if (word && word.word) {
|
||||
const currentWord = word.word
|
||||
|
||||
const suggestions = cssClasses
|
||||
.filter((className) => className.includes(currentWord))
|
||||
.map((className) => ({
|
||||
label: className,
|
||||
kind: languages.CompletionItemKind.Class,
|
||||
insertText: className,
|
||||
documentation: 'Custom CSS class',
|
||||
range: range
|
||||
}))
|
||||
|
||||
return { suggestions }
|
||||
}
|
||||
|
||||
return { suggestions: [] }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
}
|
||||
|
||||
async function listTokens(): Promise<void> {
|
||||
tokens = await UserService.listTokens({ excludeEphemeral: true })
|
||||
tokens = await UserService.listTokens()
|
||||
}
|
||||
|
||||
async function deleteToken(tokenPrefix: string) {
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { getImageDataURL, initCss } from '../../utils'
|
||||
import { initCss } from '../../utils'
|
||||
import { components } from '../../editor/component'
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
import ResolveStyle from '../helpers/ResolveStyle.svelte'
|
||||
import { ArrowDown } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { loadIcon } from '../icon'
|
||||
import Loader from '../helpers/Loader.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -29,15 +28,13 @@
|
||||
|
||||
let iconComponent: any
|
||||
|
||||
$: isIcon && resolvedConfig?.media?.configuration?.icon.icon && handleIcon()
|
||||
$: resolvedConfig.icon && handleIcon()
|
||||
|
||||
async function handleIcon() {
|
||||
if (resolvedConfig?.media?.configuration?.icon.icon) {
|
||||
iconComponent = await loadIcon(resolvedConfig.media.configuration.icon.icon)
|
||||
if (resolvedConfig.icon) {
|
||||
iconComponent = await loadIcon(resolvedConfig.icon)
|
||||
}
|
||||
}
|
||||
|
||||
$: isIcon = resolvedConfig.media?.selected == 'icon'
|
||||
</script>
|
||||
|
||||
{#each Object.keys(components['statcomponent'].initialData.configuration) as key (key)}
|
||||
@@ -60,60 +57,16 @@
|
||||
{/each}
|
||||
|
||||
{#if render}
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex flex-row gap-4 items-center p-4 rounded-md shadow-md h-full',
|
||||
css?.container?.class,
|
||||
'wm-statistic-card-container'
|
||||
)}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex items-center justify-center w-12 h-12 border rounded-md p-2 text-black',
|
||||
css?.media?.class,
|
||||
'wm-statistic-card-media'
|
||||
)}
|
||||
style={css?.media?.style}
|
||||
>
|
||||
{#if isIcon}
|
||||
{#if resolvedConfig.media && iconComponent}
|
||||
<svelte:component this={iconComponent} size={24} />
|
||||
{/if}
|
||||
{:else}
|
||||
<Loader loading={resolvedConfig.media.configuration.image.source == undefined}>
|
||||
<img
|
||||
on:pointerdown|preventDefault
|
||||
src={getImageDataURL(
|
||||
resolvedConfig?.media?.configuration?.image?.sourceKind,
|
||||
resolvedConfig?.media?.configuration?.image?.source
|
||||
)}
|
||||
alt={resolvedConfig.title}
|
||||
/>
|
||||
</Loader>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="w-full">
|
||||
<div
|
||||
class={twMerge(
|
||||
'font-normal text-primary leading-none',
|
||||
css?.title?.class,
|
||||
'wm-statistic-card-title'
|
||||
)}
|
||||
style={css?.title?.style}
|
||||
>
|
||||
{resolvedConfig.title}
|
||||
<div class="p-6 border rounded-md h-full flex flex-row gap-6 w-full items-center">
|
||||
{#if resolvedConfig.icon && iconComponent}
|
||||
<div class="bg-blue-500 rounded-md p-2">
|
||||
<svelte:component this={iconComponent} size={24} color="white" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="w-full">
|
||||
<div class="text-base font-normal text-primary">{resolvedConfig.title}</div>
|
||||
<div class="mt-1 flex items-baseline justify-between">
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex items-baseline text-2xl leading-none font-semibold text-blue-600 dark:text-blue-200',
|
||||
css?.value?.class,
|
||||
'wm-statistic-card-value'
|
||||
)}
|
||||
style={css?.value?.style}
|
||||
>
|
||||
<div class="flex items-baseline text-2xl font-semibold text-blue-600 dark:text-blue-200">
|
||||
{resolvedConfig?.value}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: $table && updateTable(resolvedConfig, searchValue)
|
||||
$: updateTable(resolvedConfig, searchValue)
|
||||
</script>
|
||||
|
||||
{#each Object.keys(components['tablecomponent'].initialData.configuration) as key (key)}
|
||||
|
||||
@@ -39,8 +39,7 @@
|
||||
MoreVertical,
|
||||
RefreshCw,
|
||||
Save,
|
||||
Smartphone,
|
||||
FileClock
|
||||
Smartphone
|
||||
} from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
@@ -82,7 +81,6 @@
|
||||
import AppTimeline from './AppTimeline.svelte'
|
||||
import type DiffDrawer from '$lib/components/DiffDrawer.svelte'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import AppReportsDrawer from './AppReportsDrawer.svelte'
|
||||
|
||||
async function hash(message) {
|
||||
try {
|
||||
@@ -513,13 +511,7 @@
|
||||
inputsDrawerOpen = true
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Schedule Reports',
|
||||
icon: FileClock,
|
||||
action: () => {
|
||||
appReportingDrawerOpen = true
|
||||
}
|
||||
},
|
||||
|
||||
...(savedApp
|
||||
? [
|
||||
{
|
||||
@@ -554,8 +546,6 @@
|
||||
}
|
||||
|
||||
let rightColumnSelect: 'timeline' | 'detail' = 'timeline'
|
||||
|
||||
let appReportingDrawerOpen = false
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
@@ -1022,8 +1012,6 @@
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<AppReportsDrawer bind:open={appReportingDrawerOpen} {appPath} />
|
||||
|
||||
<div
|
||||
class="border-b flex flex-row justify-between py-1 gap-2 gap-y-2 px-2 items-center overflow-y-visible overflow-x-auto"
|
||||
>
|
||||
|
||||
@@ -170,7 +170,6 @@
|
||||
class="{$$props.class} {lockedClasses} {width} h-full bg-surface {app.fullscreen
|
||||
? ''
|
||||
: 'max-w-7xl'} mx-auto"
|
||||
id="app-content"
|
||||
>
|
||||
{#if $appStore.grid}
|
||||
<div
|
||||
|
||||
@@ -1,532 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { enterpriseLicense } from '$lib/stores'
|
||||
import CronInput from '$lib/components/CronInput.svelte'
|
||||
import ScriptPicker from '$lib/components/ScriptPicker.svelte'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
import Tab from '$lib/components/common/tabs/Tab.svelte'
|
||||
import Tabs from '$lib/components/common/tabs/Tabs.svelte'
|
||||
import {
|
||||
FlowService,
|
||||
RawScript,
|
||||
ScheduleService,
|
||||
SettingService,
|
||||
WorkspaceService
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { emptyString, formatCron, sendUserToast } from '$lib/utils'
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { RotateCw, Save } from 'lucide-svelte'
|
||||
import { CUSTOM_TAGS_SETTING, WORKSPACE_SLACK_BOT_TOKEN_PATH } from '$lib/consts'
|
||||
import { loadSchemaFromPath } from '$lib/infer'
|
||||
export let appPath: string
|
||||
export let open = false
|
||||
|
||||
let appReportingEnabled = false
|
||||
let appReportingStartupDuration = 5
|
||||
let appReportingSchedule: {
|
||||
cron: string
|
||||
timezone: string
|
||||
} = {
|
||||
cron: '0 0 12 * *',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
}
|
||||
let selectedTab: 'email' | 'slack' | 'discord' | 'custom' = 'custom'
|
||||
let customPath: string | undefined = undefined
|
||||
let customPathSchema: Record<string, any> = {}
|
||||
let args: Record<string, any> = {}
|
||||
let areArgsValid = true
|
||||
|
||||
$: customPath
|
||||
? loadSchemaFromPath(customPath).then((schema) => {
|
||||
customPathSchema = schema
|
||||
? {
|
||||
...schema,
|
||||
properties: Object.fromEntries(
|
||||
Object.entries(schema.properties ?? {}).filter(
|
||||
([key, _]) => key !== 'pdf' && key !== 'app_path'
|
||||
)
|
||||
)
|
||||
}
|
||||
: {}
|
||||
})
|
||||
: (customPathSchema = {})
|
||||
|
||||
let isSlackConnectedWorkspace = false
|
||||
async function getWorspaceSlackSetting() {
|
||||
const settings = await WorkspaceService.getSettings({
|
||||
workspace: $workspaceStore!
|
||||
})
|
||||
if (settings.slack_name) {
|
||||
isSlackConnectedWorkspace = true
|
||||
} else {
|
||||
isSlackConnectedWorkspace = false
|
||||
}
|
||||
}
|
||||
getWorspaceSlackSetting()
|
||||
|
||||
async function getAppReportingInfo() {
|
||||
const flowPath = appPath + '_reports'
|
||||
try {
|
||||
const flow = await FlowService.getFlowByPath({
|
||||
workspace: $workspaceStore!,
|
||||
path: flowPath
|
||||
})
|
||||
const schedule = await ScheduleService.getSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
path: flowPath
|
||||
})
|
||||
appReportingSchedule = {
|
||||
cron: schedule.schedule,
|
||||
timezone: schedule.timezone
|
||||
}
|
||||
appReportingStartupDuration ??= schedule.args?.startup_duration
|
||||
args = schedule.args
|
||||
? Object.fromEntries(
|
||||
Object.entries(schedule.args).filter(
|
||||
([key, _]) => key !== 'app_path' && key !== 'startup_duration'
|
||||
)
|
||||
)
|
||||
: {}
|
||||
|
||||
selectedTab =
|
||||
flow.value.modules[1]?.value.type === 'script'
|
||||
? flow.value.modules[1].value.path === notificationScripts.email.path
|
||||
? 'email'
|
||||
: flow.value.modules[1].value.path === notificationScripts.slack.path
|
||||
? 'slack'
|
||||
: flow.value.modules[1].value.path === notificationScripts.discord.path
|
||||
? 'discord'
|
||||
: 'custom'
|
||||
: 'custom'
|
||||
|
||||
customPath =
|
||||
selectedTab === 'custom' &&
|
||||
flow.value.modules[1]?.value.type === 'script' &&
|
||||
!flow.value.modules[1].value.path.startsWith('hub/')
|
||||
? flow.value.modules[1].value.path
|
||||
: undefined
|
||||
|
||||
appReportingEnabled = true
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
$: appPath && getAppReportingInfo()
|
||||
|
||||
async function disableAppReporting(skipToast = false) {
|
||||
const flowPath = appPath + '_reports'
|
||||
await ScheduleService.deleteSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
path: flowPath
|
||||
})
|
||||
await FlowService.deleteFlowByPath({
|
||||
workspace: $workspaceStore!,
|
||||
path: flowPath
|
||||
})
|
||||
if (!skipToast) {
|
||||
appReportingEnabled = false
|
||||
sendUserToast('App reporting disabled')
|
||||
}
|
||||
}
|
||||
|
||||
const pdfPreviewScript = `import puppeteer from \'puppeteer-core\';
|
||||
export async function main(app_path: string, startup_duration = 5) {
|
||||
const browser = await puppeteer.launch({ headless: \'new\', executablePath: \'/usr/bin/chromium\', args: [\'--no-sandbox\'] });
|
||||
const page = await browser.newPage();
|
||||
await page.setCookie({
|
||||
"name": "token",
|
||||
"value": Bun.env["WM_TOKEN"],
|
||||
"domain": Bun.env["WM_BASE_URL"]?.replace(/https?:\\/\\//, \'\')
|
||||
})
|
||||
await page.goto(Bun.env["WM_BASE_URL"] + \'/apps/get/\' + app_path + \'?workspace=\' + Bun.env["WM_WORKSPACE"]);
|
||||
await new Promise((resolve, _) => {
|
||||
setTimeout(resolve, startup_duration * 1000)
|
||||
})
|
||||
await page.$eval("#sidebar", el => el.remove())
|
||||
await page.$eval("#content", el => el.classList.remove("md:pl-12"))
|
||||
await page.$eval("#app-edit-btn", el => el.remove())
|
||||
const elem = await page.$(\'#app-content\');
|
||||
const { height } = await elem.boundingBox();
|
||||
await page.setViewport({ width: 1200, height });
|
||||
await new Promise((resolve, _) => {
|
||||
setTimeout(resolve, 200)
|
||||
})
|
||||
const pdf = await page.pdf({
|
||||
printBackground: true,
|
||||
width: 1200,
|
||||
height
|
||||
});
|
||||
await browser.close();
|
||||
return Buffer.from(pdf).toString(\'base64\');
|
||||
}`
|
||||
|
||||
const notificationScripts = {
|
||||
discord: {
|
||||
path: 'hub/7772/discord',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
discord_webhook: {
|
||||
type: 'object',
|
||||
format: 'resource-discord_webhook',
|
||||
properties: {},
|
||||
required: [],
|
||||
description: ''
|
||||
}
|
||||
},
|
||||
required: ['discord_webhook']
|
||||
}
|
||||
},
|
||||
slack: {
|
||||
path: 'hub/7771/slack', // if to be updated, also update it in in backend/windmill-queue/src/jobs.rs
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
channel: {
|
||||
type: 'string',
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
required: ['channel']
|
||||
}
|
||||
},
|
||||
email: {
|
||||
path: 'hub/7774/smtp',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
smtp: {
|
||||
type: 'object',
|
||||
format: 'resource-smtp',
|
||||
properties: {},
|
||||
required: [],
|
||||
description: ''
|
||||
},
|
||||
from_email: {
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
to_email: {
|
||||
type: 'string',
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
required: ['smtp', 'from_email', 'to_email']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAppReporting() {
|
||||
await disableAppReporting(true)
|
||||
await enableAppReporting(true)
|
||||
sendUserToast('App reporting updated')
|
||||
}
|
||||
|
||||
async function enableAppReporting(skipToast = false) {
|
||||
const flowPath = appPath + '_reports'
|
||||
|
||||
try {
|
||||
// will only work if the user is super admin
|
||||
const customTags = ((await SettingService.getGlobal({
|
||||
key: CUSTOM_TAGS_SETTING
|
||||
})) ?? []) as string[]
|
||||
|
||||
if (!customTags.includes('chromium')) {
|
||||
await SettingService.setGlobal({
|
||||
key: CUSTOM_TAGS_SETTING,
|
||||
requestBody: {
|
||||
value: [...customTags, 'chromium']
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
const inputTransforms: {
|
||||
[key: string]: {
|
||||
expr: string
|
||||
type: 'javascript'
|
||||
}
|
||||
} = {
|
||||
app_path: {
|
||||
type: 'javascript',
|
||||
expr: 'flow_input.app_path'
|
||||
},
|
||||
pdf: {
|
||||
type: 'javascript',
|
||||
expr: 'results.a'
|
||||
},
|
||||
...Object.fromEntries(
|
||||
Object.keys(args).map((key) => [
|
||||
key,
|
||||
{
|
||||
type: 'javascript',
|
||||
expr: `flow_input.${key}`
|
||||
}
|
||||
])
|
||||
),
|
||||
...(selectedTab === 'slack'
|
||||
? {
|
||||
slack: {
|
||||
type: 'javascript',
|
||||
expr: 'flow_input.slack'
|
||||
}
|
||||
}
|
||||
: {})
|
||||
}
|
||||
await FlowService.createFlow({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
summary: appPath + ' - Reports flow',
|
||||
value: {
|
||||
modules: [
|
||||
{
|
||||
id: 'a',
|
||||
value: {
|
||||
type: 'rawscript',
|
||||
tag: 'chromium',
|
||||
content: pdfPreviewScript,
|
||||
language: RawScript.language.BUN,
|
||||
input_transforms: {
|
||||
app_path: {
|
||||
expr: 'flow_input.app_path',
|
||||
type: 'javascript'
|
||||
},
|
||||
startup_duration: {
|
||||
expr: 'flow_input.startup_duration',
|
||||
type: 'javascript'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'b',
|
||||
value: {
|
||||
type: 'script',
|
||||
path:
|
||||
selectedTab === 'custom'
|
||||
? customPath || ''
|
||||
: notificationScripts[selectedTab].path,
|
||||
input_transforms: inputTransforms
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
schema: {
|
||||
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
||||
properties: {
|
||||
app_path: {
|
||||
description: '',
|
||||
type: 'string',
|
||||
default: null,
|
||||
format: ''
|
||||
},
|
||||
startup_duration: {
|
||||
description: '',
|
||||
type: 'integer',
|
||||
default: 5,
|
||||
format: ''
|
||||
},
|
||||
...(selectedTab === 'custom'
|
||||
? customPathSchema.properties
|
||||
: notificationScripts[selectedTab].schema.properties),
|
||||
...(selectedTab === 'slack'
|
||||
? {
|
||||
slack: {
|
||||
description: '',
|
||||
type: 'object',
|
||||
format: 'resource-slack',
|
||||
properties: {},
|
||||
required: []
|
||||
}
|
||||
}
|
||||
: {})
|
||||
},
|
||||
required: [
|
||||
'app_path',
|
||||
'startup_duration',
|
||||
...(selectedTab === 'custom'
|
||||
? customPathSchema.required
|
||||
: notificationScripts[selectedTab].schema.required),
|
||||
...(selectedTab === 'slack' ? ['slack'] : [])
|
||||
],
|
||||
type: 'object'
|
||||
},
|
||||
path: flowPath
|
||||
}
|
||||
})
|
||||
|
||||
await ScheduleService.createSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path: flowPath,
|
||||
schedule: formatCron(appReportingSchedule.cron),
|
||||
timezone: appReportingSchedule.timezone,
|
||||
script_path: flowPath,
|
||||
is_flow: true,
|
||||
args: {
|
||||
app_path: appPath,
|
||||
startup_duration: appReportingStartupDuration,
|
||||
...args,
|
||||
...(selectedTab === 'slack'
|
||||
? {
|
||||
slack: '$res:' + WORKSPACE_SLACK_BOT_TOKEN_PATH
|
||||
}
|
||||
: {})
|
||||
},
|
||||
enabled: true
|
||||
}
|
||||
})
|
||||
appReportingEnabled = true
|
||||
if (!skipToast) {
|
||||
sendUserToast('App reporting enabled')
|
||||
}
|
||||
}
|
||||
|
||||
let disabled = true
|
||||
$: disabled =
|
||||
emptyString(appReportingSchedule.cron) ||
|
||||
(selectedTab === 'custom' && emptyString(customPath)) ||
|
||||
(selectedTab === 'slack' && !isSlackConnectedWorkspace) ||
|
||||
!areArgsValid
|
||||
</script>
|
||||
|
||||
<Drawer bind:open>
|
||||
<DrawerContent title="Schedule Reports" on:close={() => (open = false)}
|
||||
><svelte:fragment slot="actions">
|
||||
<div class="mr-4 center-center -mt-2">
|
||||
<Toggle
|
||||
checked={appReportingEnabled}
|
||||
options={{ right: 'enable', left: 'disable' }}
|
||||
on:change={async () => {
|
||||
if (appReportingEnabled) {
|
||||
disableAppReporting()
|
||||
} else {
|
||||
enableAppReporting()
|
||||
}
|
||||
}}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
color="dark"
|
||||
startIcon={{ icon: Save }}
|
||||
size="sm"
|
||||
on:click={async () => {
|
||||
appReportingEnabled ? await updateAppReporting() : await enableAppReporting()
|
||||
open = false
|
||||
}}
|
||||
{disabled}
|
||||
>
|
||||
{appReportingEnabled ? 'Update' : 'Save and enable'}
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col gap-8">
|
||||
<Alert type="info" title="Scheduled PDF reports"
|
||||
>Send a PDF preview of the app at a given schedule. Enabling this feature will create a flow
|
||||
and a schedule in your workspace.
|
||||
<br /><br />
|
||||
For the flow to be executed, you need to set the WORKER_GROUP environment variable of one of
|
||||
your workers to "reports" or add the tag "chromium" to one of your worker groups.
|
||||
</Alert>
|
||||
|
||||
<Section label="Reporting schedule">
|
||||
<CronInput
|
||||
bind:schedule={appReportingSchedule.cron}
|
||||
bind:timezone={appReportingSchedule.timezone}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
label="Startup duration in seconds"
|
||||
tooltip="The number of seconds to wait before capturing a preview to ensure that all startup scripts
|
||||
have been executed."
|
||||
>
|
||||
<div class="w-full pt-2">
|
||||
<input
|
||||
type="number"
|
||||
class="text-sm w-full font-semibold"
|
||||
bind:value={appReportingStartupDuration}
|
||||
/>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section label="Notification">
|
||||
<Tabs bind:selected={selectedTab}>
|
||||
<Tab value="custom">Custom</Tab>
|
||||
<Tab value="email" disabled={!$enterpriseLicense}>
|
||||
<div class="flex flex-row gap-1 items-center"
|
||||
>Email{!$enterpriseLicense ? ' (EE only)' : ''}</div
|
||||
>
|
||||
</Tab>
|
||||
<Tab value="slack" disabled={!$enterpriseLicense}
|
||||
>Slack{!$enterpriseLicense ? ' (EE only)' : ''}</Tab
|
||||
>
|
||||
<Tab value="discord" disabled={!$enterpriseLicense}
|
||||
>Discord{!$enterpriseLicense ? ' (EE only)' : ''}</Tab
|
||||
>
|
||||
</Tabs>
|
||||
{#if selectedTab === 'custom'}
|
||||
<div class="pt-2">
|
||||
<ScriptPicker
|
||||
on:select={(ev) => {
|
||||
customPath = ev.detail.path
|
||||
}}
|
||||
initialPath={customPath}
|
||||
allowRefresh
|
||||
/>
|
||||
</div>
|
||||
<div class="prose text-2xs text-tertiary mt-2">
|
||||
Pick a script that does whatever with the PDF report.
|
||||
|
||||
<br />
|
||||
|
||||
The script chosen is passed the parameters `pdf: string` and `app_path: string` where
|
||||
`pdf` is the base64 encoded PDF report and `app_path` is the path of the app being
|
||||
reported.
|
||||
</div>
|
||||
{/if}
|
||||
{#if selectedTab === 'slack'}
|
||||
<div class="pt-4">
|
||||
{#if isSlackConnectedWorkspace}
|
||||
<Alert type="info" title="Will use the Slack resource linked to the workspace" />
|
||||
{:else}
|
||||
<Alert type="error" title="Workspace not connected to Slack">
|
||||
<div class="flex flex-row gap-x-1 w-full items-center">
|
||||
<p class="text-clip grow min-w-0">
|
||||
The workspace needs to be connected to Slack to use this feature. You can <a
|
||||
target="_blank"
|
||||
href="/workspace_settings?tab=slack">configure it here</a
|
||||
>.
|
||||
</p>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
on:click={getWorspaceSlackSetting}
|
||||
startIcon={{ icon: RotateCw }}
|
||||
/>
|
||||
</div>
|
||||
</Alert>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="w-full pt-4">
|
||||
{#if selectedTab !== 'custom' || customPath !== undefined}
|
||||
{#key selectedTab + JSON.stringify(customPathSchema ?? {})}
|
||||
<SchemaForm
|
||||
bind:isValid={areArgsValid}
|
||||
bind:args
|
||||
schema={selectedTab !== 'custom'
|
||||
? notificationScripts[selectedTab].schema
|
||||
: customPathSchema}
|
||||
/>
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
@@ -2721,22 +2721,7 @@ This is a paragraph.
|
||||
icon: FileBarChart,
|
||||
documentationLink: `${documentationBaseUrl}/statistic_card`,
|
||||
dims: '2:4-3:4' as AppComponentDimensions,
|
||||
customCss: {
|
||||
title: {
|
||||
class: '',
|
||||
style: '',
|
||||
quickCss: ['font-size: 1rem', 'font-size: 1.5rem', 'font-size: 2rem'],
|
||||
quickTailwindClasses: ['text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl']
|
||||
},
|
||||
container: { class: '', style: '' },
|
||||
value: {
|
||||
class: '',
|
||||
style: '',
|
||||
quickCss: ['font-size: 1rem', 'font-size: 1.5rem', 'font-size: 2rem'],
|
||||
quickTailwindClasses: ['text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl']
|
||||
},
|
||||
media: { class: '', style: '' }
|
||||
},
|
||||
customCss: {},
|
||||
initialData: {
|
||||
configuration: {
|
||||
title: {
|
||||
@@ -2754,40 +2739,11 @@ This is a paragraph.
|
||||
value: 0,
|
||||
fieldType: 'number'
|
||||
},
|
||||
media: {
|
||||
type: 'oneOf',
|
||||
selected: 'image',
|
||||
labels: {
|
||||
icon: 'Icon',
|
||||
image: 'Image'
|
||||
},
|
||||
configuration: {
|
||||
icon: {
|
||||
icon: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'icon-select'
|
||||
}
|
||||
},
|
||||
image: {
|
||||
source: {
|
||||
type: 'static',
|
||||
value: '/logo.svg',
|
||||
fieldType: 'text',
|
||||
fileUpload: {
|
||||
accept: 'image/*',
|
||||
convertTo: 'base64'
|
||||
}
|
||||
},
|
||||
sourceKind: {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
selectOptions: selectOptions.imageSourceKind,
|
||||
value: 'url' as (typeof selectOptions.imageSourceKind)[number]
|
||||
}
|
||||
}
|
||||
}
|
||||
} as const
|
||||
icon: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'icon-select'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import CssEval from './CssEval.svelte'
|
||||
import parse from 'style-to-object'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
|
||||
export let name: string
|
||||
export let value: ComponentCssProperty = {}
|
||||
@@ -47,207 +46,169 @@
|
||||
}
|
||||
|
||||
let dynamicClass: boolean = value?.evalClass !== undefined
|
||||
let render = 0
|
||||
</script>
|
||||
|
||||
{#key render}
|
||||
<div class=" border-b flex justify-between items-center p-2 text-xs leading-6 font-bold">
|
||||
<div class="flex flex-col gap-1 w-full items-start">
|
||||
<div class="flex flex-row h-8 items-center justify-between w-full">
|
||||
<div class="capitalize">
|
||||
{addWhitespaceBeforeCapitals(name)}
|
||||
</div>
|
||||
{#if shouldDisplayLeft}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
dispatch('left')
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2 text-2xs items-center">
|
||||
<MoveLeft size={14} />
|
||||
Copy for this component
|
||||
</div>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if shouldDisplayRight}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
dispatch('right')
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2 text-2xs items-center">
|
||||
Copy for every {componentType ? ccomponents[componentType].name : 'component'}
|
||||
<MoveRight size={14} />
|
||||
</div>
|
||||
</Button>
|
||||
{/if}
|
||||
<div class=" border-b flex justify-between items-center p-2 text-xs leading-6 font-bold">
|
||||
<div class="flex flex-col gap-1 w-full items-start">
|
||||
<div class="flex flex-row h-8 items-center justify-between w-full">
|
||||
<div class="capitalize">
|
||||
{addWhitespaceBeforeCapitals(name)}
|
||||
</div>
|
||||
{#if wmClass}
|
||||
<Badge small>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
{wmClass}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
on:click={() => {
|
||||
copyToClipboard(wmClass)
|
||||
}}
|
||||
>
|
||||
<Copy size={14} />
|
||||
</Button>
|
||||
{#if shouldDisplayLeft}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
dispatch('left')
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2 text-2xs items-center">
|
||||
<MoveLeft size={14} />
|
||||
Copy for this component
|
||||
</div>
|
||||
</Badge>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if shouldDisplayRight}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
dispatch('right')
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2 text-2xs items-center">
|
||||
Copy for every {componentType ? ccomponents[componentType].name : 'component'}
|
||||
<MoveRight size={14} />
|
||||
</div>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if wmClass}
|
||||
<Badge small>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
{wmClass}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
on:click={() => {
|
||||
copyToClipboard(wmClass)
|
||||
}}
|
||||
>
|
||||
<Copy size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if value}
|
||||
<div class="p-2 flex flex-col">
|
||||
{#if tooltip}
|
||||
<div class="text-tertiary text-2xs py-2">{tooltip}</div>
|
||||
{/if}
|
||||
{#if value.style !== undefined || forceStyle}
|
||||
<div class="pb-2">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label class="block w-full">
|
||||
<div class="flex flex-row justify-between items-center w-full h-8">
|
||||
<div class="text-xs font-medium text-tertiary"> Plain CSS </div>
|
||||
{#if overriden}
|
||||
<Badge color="red" small>Overriden by local</Badge>
|
||||
{:else if overridding}
|
||||
<Badge color="blue" small>Overriding global</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
{#if value}
|
||||
<div class="p-2">
|
||||
{#if tooltip}
|
||||
<div class="text-tertiary text-2xs py-2">{tooltip}</div>
|
||||
{/if}
|
||||
{#if value.style !== undefined || forceStyle}
|
||||
<div class="pb-2">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label class="block w-full">
|
||||
<div class="flex flex-row justify-between items-center w-full h-8">
|
||||
<div class="text-xs font-medium text-tertiary"> Plain CSS </div>
|
||||
{#if overriden}
|
||||
<Badge color="red" small>Overriden by local</Badge>
|
||||
{:else if overridding}
|
||||
<Badge color="blue" small>Overriding global</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-1">
|
||||
<div class="relative grow">
|
||||
<ClearableInput
|
||||
bind:value={value.style}
|
||||
type="textarea"
|
||||
wrapperClass="h-full min-h-[72px]"
|
||||
inputClass="h-full !text-xs !rounded-none !p-2"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
{#if quickStyleProperties?.length}
|
||||
<Popover placement="bottom" notClickable disappearTimeout={0}>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="!p-1 !w-[34px] !h-[34px] {isQuickMenuOpen
|
||||
? '!bg-gray-200/60 hover:!bg-gray-200 focus:!bg-gray-200'
|
||||
: ''}"
|
||||
aria-label="{isQuickMenuOpen ? 'Close' : 'Open'} styling menu"
|
||||
on:click={toggleQuickMenu}
|
||||
>
|
||||
<Paintbrush2 size={18} />
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
{isQuickMenuOpen ? 'Close' : 'Open'} styling menu
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
{#if quickStyleProperties?.length && isQuickMenuOpen}
|
||||
<div transition:fade|local={{ duration: 200 }} class="w-full pt-1">
|
||||
<QuickStyleMenu
|
||||
<div class="flex gap-1">
|
||||
<div class="relative grow">
|
||||
<ClearableInput
|
||||
bind:value={value.style}
|
||||
properties={quickStyleProperties}
|
||||
{componentType}
|
||||
componentProperty={name}
|
||||
type="textarea"
|
||||
wrapperClass="h-full min-h-[72px]"
|
||||
inputClass="h-full !text-xs !rounded-none !p-2"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if value.quickCss}
|
||||
<div class="flex flex-row gap-1 items-center mt-1 flex-wrap">
|
||||
{#each value.quickCss as v}
|
||||
<Badge
|
||||
small
|
||||
baseClass="cursor-pointer"
|
||||
on:click={() => {
|
||||
value.style = value.style === '' ? `${v};` : `${value.style} ${v};`
|
||||
}}
|
||||
>
|
||||
{v}
|
||||
</Badge>
|
||||
{/each}
|
||||
<div class="flex flex-col gap-1">
|
||||
{#if quickStyleProperties?.length}
|
||||
<Popover placement="bottom" notClickable disappearTimeout={0}>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="!p-1 !w-[34px] !h-[34px] {isQuickMenuOpen
|
||||
? '!bg-gray-200/60 hover:!bg-gray-200 focus:!bg-gray-200'
|
||||
: ''}"
|
||||
aria-label="{isQuickMenuOpen ? 'Close' : 'Open'} styling menu"
|
||||
on:click={toggleQuickMenu}
|
||||
>
|
||||
<Paintbrush2 size={18} />
|
||||
</Button>
|
||||
<svelte:fragment slot="text">
|
||||
{isQuickMenuOpen ? 'Close' : 'Open'} styling menu
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if value.class !== undefined || forceClass}
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label class="block">
|
||||
<div class="text-xs font-medium text-tertiary">
|
||||
Tailwind classes
|
||||
<Tooltip light documentationLink="https://tailwindcss.com/">
|
||||
Use any tailwind classes to style your component
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<SimpleEditor
|
||||
class="h-24"
|
||||
lang="tailwindcss"
|
||||
bind:code={value.class}
|
||||
fixedOverflowWidgets={true}
|
||||
small
|
||||
automaticLayout
|
||||
deno={false}
|
||||
subType="tailwind"
|
||||
</label>
|
||||
{#if quickStyleProperties?.length && isQuickMenuOpen}
|
||||
<div transition:fade|local={{ duration: 200 }} class="w-full pt-1">
|
||||
<QuickStyleMenu
|
||||
bind:value={value.style}
|
||||
properties={quickStyleProperties}
|
||||
{componentType}
|
||||
componentProperty={name}
|
||||
/>
|
||||
</div>
|
||||
{#if value.quickTailwindClasses}
|
||||
<div class="flex flex-row gap-1 items-center mt-1 flex-wrap">
|
||||
{#each value.quickTailwindClasses as cls}
|
||||
<Badge
|
||||
baseClass="cursor-pointer"
|
||||
small
|
||||
on:click={() => {
|
||||
value.class = value.class === '' ? cls : `${value.class} ${cls}`
|
||||
render++
|
||||
}}
|
||||
>
|
||||
{cls}
|
||||
</Badge>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</label>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Toggle
|
||||
options={{
|
||||
right: 'Use dynamic class'
|
||||
}}
|
||||
size="xs"
|
||||
bind:checked={dynamicClass}
|
||||
on:change={(e) => {
|
||||
if (e.detail && !value.evalClass) {
|
||||
value.evalClass = {
|
||||
type: 'evalv2',
|
||||
expr: '',
|
||||
connections: [],
|
||||
fieldType: 'text'
|
||||
}
|
||||
} else {
|
||||
value.evalClass = undefined
|
||||
{#if value.class !== undefined || forceClass}
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label class="block">
|
||||
<div class="text-xs font-medium text-tertiary">
|
||||
Tailwind classes
|
||||
<Tooltip light documentationLink="https://tailwindcss.com/">
|
||||
Use any tailwind classes to style your component
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<ClearableInput
|
||||
bind:value={value.class}
|
||||
type="textarea"
|
||||
wrapperClass="h-full"
|
||||
inputClass="h-full !text-xs !rounded-none !p-2 min-h-[72px]"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
{/if}
|
||||
|
||||
<Toggle
|
||||
options={{
|
||||
right: 'Use dynamic class'
|
||||
}}
|
||||
size="xs"
|
||||
bind:checked={dynamicClass}
|
||||
on:change={(e) => {
|
||||
if (e.detail && !value.evalClass) {
|
||||
value.evalClass = {
|
||||
type: 'evalv2',
|
||||
expr: '',
|
||||
connections: [],
|
||||
fieldType: 'text'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
} else {
|
||||
value.evalClass = undefined
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if value?.evalClass && dynamicClass}
|
||||
<CssEval bind:evalClass={value.evalClass} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
{#if value?.evalClass && dynamicClass}
|
||||
<CssEval bind:evalClass={value.evalClass} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
import * as csstree from 'css-tree'
|
||||
import type { ComponentCssProperty } from '../../types'
|
||||
|
||||
export function sanitizeCss(css: string, authorizedClassNames: string[]) {
|
||||
const ast = csstree.parse(css)
|
||||
const removedClassNames: string[] = []
|
||||
|
||||
csstree.walk(ast, (node: any, item, list) => {
|
||||
if (node.type === 'Rule') {
|
||||
let shouldRemoveRule = true
|
||||
|
||||
csstree.walk(node, (innerNode: any) => {
|
||||
if (innerNode.type === 'ClassSelector' && authorizedClassNames.includes(innerNode.name)) {
|
||||
shouldRemoveRule = false
|
||||
}
|
||||
if (shouldRemoveRule && innerNode.name) {
|
||||
removedClassNames.push(innerNode.name)
|
||||
}
|
||||
})
|
||||
|
||||
if (shouldRemoveRule) {
|
||||
list.remove(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
css: csstree.generate(ast),
|
||||
removedClassNames
|
||||
}
|
||||
}
|
||||
|
||||
export const authorizedClassnames = [
|
||||
'wm-container',
|
||||
'wm-list',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,7 +89,6 @@
|
||||
placeholder={config?.['placeholder']}
|
||||
customTitle={config?.['customTitle']}
|
||||
tooltip={config?.['tooltip']}
|
||||
fileUpload={config?.['fileUpload']}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -44,8 +44,6 @@ export type ComponentCssProperty = {
|
||||
class?: string
|
||||
style?: string
|
||||
evalClass?: RichConfiguration
|
||||
quickCss?: readonly string[] | undefined
|
||||
quickTailwindClasses?: readonly string[] | undefined
|
||||
}
|
||||
|
||||
export type ComponentCustomCSS<T extends keyof typeof components> = Partial<
|
||||
@@ -175,7 +173,7 @@ export type ListContext = Writable<{
|
||||
disabled: boolean
|
||||
}>
|
||||
|
||||
export type ListInputs = { set: (id: string, value: any) => void; remove: (id: string) => void }
|
||||
export type ListInputs = {set: (id: string, value: any) => void, remove: (id: string) => void}
|
||||
|
||||
export type GroupContext = Writable<Record<string, any>>
|
||||
|
||||
|
||||
@@ -365,20 +365,3 @@ export function transformBareBase64IfNecessary(source: string | undefined) {
|
||||
return `data:application/octet-stream;base64,${source}`
|
||||
}
|
||||
}
|
||||
|
||||
export function getImageDataURL(imageKind: string | undefined, image: string | undefined) {
|
||||
if (!imageKind || !image) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (imageKind) {
|
||||
case 'png encoded as base64':
|
||||
return 'data:image/png;base64,' + image
|
||||
case 'jpeg encoded as base64':
|
||||
return 'data:image/jpeg;base64,' + image
|
||||
case 'svg encoded as base64':
|
||||
return 'data:image/svg+xml;base64,' + image
|
||||
default:
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<svelte:component this={icons[type]} class={classes[type].iconClass} size={16} />
|
||||
</div>
|
||||
|
||||
<div class="ml-2 pt-[0.15rem] w-full">
|
||||
<div class="ml-2 w-full">
|
||||
<span
|
||||
class={classNames(
|
||||
size === 'sm' ? 'text-sm' : 'text-xs ',
|
||||
@@ -72,17 +72,15 @@
|
||||
<Tooltip {documentationLink} scale={0.9}>{tooltip}</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
{#if $$slots.default}
|
||||
<div
|
||||
class={classNames(
|
||||
size === 'sm' ? 'text-sm' : 'text-xs ',
|
||||
'mt-2',
|
||||
classes[type].descriptionClass
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class={classNames(
|
||||
size === 'sm' ? 'text-sm' : 'text-xs ',
|
||||
'mt-2',
|
||||
classes[type].descriptionClass
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -149,7 +149,14 @@
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
allowedPlacements: ['bottom-end', 'top-end']
|
||||
allowedPlacements: [
|
||||
'bottom-start',
|
||||
'bottom-end',
|
||||
'top-start',
|
||||
'top-end',
|
||||
'top',
|
||||
'bottom'
|
||||
]
|
||||
})
|
||||
]
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
system: |-
|
||||
You are a helpful coding assistant for Windmill, a developer platform for running scripts. You modify code as instructed by the user. Each user message includes some contextual information which should guide your answer.
|
||||
You are a helpful coding assistant for Windmill, a developer platform for running scripts. You fix the code shared by the user. Each user message includes some contextual information which should guide your answer.
|
||||
Only output code. Wrap the code in a code block.
|
||||
Put explanations directly in the code as comments.
|
||||
Return the complete modified code.
|
||||
|
||||
Here's how interactions have to look like:
|
||||
user: {sample_question}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const EDIT_PROMPT = {
|
||||
"system": "You are a helpful coding assistant for Windmill, a developer platform for running scripts. You modify code as instructed by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nPut explanations directly in the code as comments.\nReturn the complete modified code.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```",
|
||||
"system": "You are a helpful coding assistant for Windmill, a developer platform for running scripts. You fix the code shared by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nPut explanations directly in the code as comments.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```",
|
||||
"prompts": {
|
||||
"python3": {
|
||||
"prompt": "Here's my python3 code: \n```python\n{code}\n```\n<contextual_information>\nYou have to write a function in python called \"main\". Specify the parameter types. Do not call the main function. You should generally return the result.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nThe resource type name has to be exactly as specified (has to be IN LOEWRCASE). If the type name conflicts with any imported methods, you have to rename the imported method with the conflicting name.\n<contextual_information>\nMy instructions: {description}"
|
||||
|
||||
@@ -3,7 +3,6 @@ system: |-
|
||||
Only output code. Wrap the code in a code block.
|
||||
Explain the error and the fix after generating the code inside an <explanation> tag.
|
||||
Also put explanations directly in the code as comments.
|
||||
Return the complete fixed code.
|
||||
|
||||
Here's how interactions have to look like:
|
||||
user: {sample_question}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const FIX_PROMPT = {
|
||||
"system": "You are a helpful coding assistant for Windmill, a developer platform for running scripts. You fix the code shared by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nExplain the error and the fix after generating the code inside an <explanation> tag.\nAlso put explanations directly in the code as comments.\nReturn the complete fixed code.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```\n<explanation>{explanation}</explanation>",
|
||||
"system": "You are a helpful coding assistant for Windmill, a developer platform for running scripts. You fix the code shared by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nExplain the error and the fix after generating the code inside an <explanation> tag.\nAlso put explanations directly in the code as comments.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```\n<explanation>{explanation}</explanation>",
|
||||
"prompts": {
|
||||
"python3": {
|
||||
"prompt": "Here's my python3 code: \n```python\n{code}\n```\n<contextual_information>\nYou have to write a function in python called \"main\". Specify the parameter types. Do not call the main function. You should generally return the result.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nThe resource type name has to be exactly as specified (has to be IN LOEWRCASE). If the type name conflicts with any imported methods, you have to rename the imported method with the conflicting name.\n<contextual_information>\nI get the following error: {error}\nFix my code."
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
import ToggleButtonGroup from '../common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import Tooltip from '../Tooltip.svelte'
|
||||
import AutoComplete from 'simple-svelte-autocomplete'
|
||||
import { AlertCircle, CheckCircle2, ChevronDown, Filter, PlayCircle, X } from 'lucide-svelte'
|
||||
import { ChevronDown, Filter, X } from 'lucide-svelte'
|
||||
import JsonEditor from '../apps/editor/settingsPanel/inputEditor/JsonEditor.svelte'
|
||||
import Toggle from '../Toggle.svelte'
|
||||
import Label from '../Label.svelte'
|
||||
import Section from '../Section.svelte'
|
||||
import CloseButton from '../common/CloseButton.svelte'
|
||||
|
||||
// Filters
|
||||
export let path: string | null = null
|
||||
@@ -22,8 +19,6 @@
|
||||
export let jobKindsCat: string
|
||||
export let user: string | null = null
|
||||
export let folder: string | null = null
|
||||
export let hideSchedules: boolean | undefined = undefined
|
||||
export let mobile: boolean = false
|
||||
|
||||
// Autocomplete data
|
||||
export let paths: string[] = []
|
||||
@@ -51,117 +46,119 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-4">
|
||||
{#if !mobile}
|
||||
<div class="flex gap-2">
|
||||
<div class="relative">
|
||||
<span class="text-xs absolute -top-4">Filter by</span>
|
||||
<ToggleButtonGroup
|
||||
bind:selected={filterBy}
|
||||
on:selected={() => {
|
||||
if (!manuallySet) {
|
||||
path = null
|
||||
user = null
|
||||
folder = null
|
||||
} else {
|
||||
manuallySet = false
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleButton value="path" label="Path" />
|
||||
<ToggleButton value="user" label="User" />
|
||||
<ToggleButton value="folder" label="Folder" />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
{#if filterBy == 'user'}
|
||||
{#key user}
|
||||
<div class="relative">
|
||||
{#if user}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
user = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs absolute -top-4">User</span>
|
||||
<AutoComplete
|
||||
items={usernames}
|
||||
value={user}
|
||||
bind:selectedItem={user}
|
||||
inputClassName="!h-[32px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={user ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
{:else if filterBy == 'folder'}
|
||||
{#key folder}
|
||||
<div class="relative">
|
||||
{#if folder}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
folder = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs absolute -top-4">Folder</span>
|
||||
|
||||
<AutoComplete
|
||||
items={folders}
|
||||
value={folder}
|
||||
bind:selectedItem={folder}
|
||||
inputClassName="!h-[32px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={folder ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
{:else if filterBy === 'path'}
|
||||
{#key path}
|
||||
<div class="relative">
|
||||
{#if path}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
path = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs absolute -top-4">Path</span>
|
||||
|
||||
<AutoComplete
|
||||
items={paths}
|
||||
value={path}
|
||||
bind:selectedItem={path}
|
||||
inputClassName="!h-[32px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={path ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
{/if}
|
||||
<div class="flex flex-col items-start gap-6 xl:gap-2 xl:flex-row mt-4 xl:mt-0">
|
||||
<div class="flex flex-col xl:flex-row gap-6 xl:gap-2 w-full">
|
||||
<div class="relative">
|
||||
<span class="text-xs absolute -top-4">Filter by</span>
|
||||
<ToggleButtonGroup
|
||||
bind:selected={filterBy}
|
||||
on:selected={() => {
|
||||
if (!manuallySet) {
|
||||
path = null
|
||||
user = null
|
||||
folder = null
|
||||
} else {
|
||||
manuallySet = false
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleButton value="path" label="Path" />
|
||||
<ToggleButton value="user" label="User" />
|
||||
<ToggleButton value="folder" label="Folder" />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if filterBy == 'user'}
|
||||
{#key user}
|
||||
<div class="relative">
|
||||
{#if user}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
user = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs absolute -top-4">User</span>
|
||||
<AutoComplete
|
||||
items={usernames}
|
||||
value={user}
|
||||
bind:selectedItem={user}
|
||||
inputClassName="!h-[30px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={user ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
{/if}
|
||||
{#if filterBy == 'folder'}
|
||||
{#key folder}
|
||||
<div class="relative">
|
||||
{#if folder}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
folder = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs absolute -top-4">Folder</span>
|
||||
|
||||
<AutoComplete
|
||||
items={folders}
|
||||
value={folder}
|
||||
bind:selectedItem={folder}
|
||||
inputClassName="!h-[30px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={folder ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
{/if}
|
||||
{#if filterBy === 'path'}
|
||||
{#key path}
|
||||
<div class="relative">
|
||||
{#if path}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
path = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs absolute -top-4">Path</span>
|
||||
|
||||
<AutoComplete
|
||||
items={paths}
|
||||
value={path}
|
||||
bind:selectedItem={path}
|
||||
inputClassName="!h-[30px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={path ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
{/if}
|
||||
<div class="flex flex-col xl:flex-row gap-6 xl:gap-2 w-full">
|
||||
<div class="relative">
|
||||
<span class="text-xs absolute -top-4">Kind</span>
|
||||
<ToggleButtonGroup bind:selected={jobKindsCat}>
|
||||
@@ -190,238 +187,84 @@
|
||||
<span class="text-xs absolute -top-4">Status</span>
|
||||
<ToggleButtonGroup bind:selected={success}>
|
||||
<ToggleButton value={undefined} label="All" />
|
||||
<ToggleButton
|
||||
value={'running'}
|
||||
label="Running"
|
||||
class="whitespace-nowrap"
|
||||
icon={PlayCircle}
|
||||
iconProps={{ color: success === 'running' ? 'blue' : 'gray' }}
|
||||
/>
|
||||
<ToggleButton
|
||||
value={'success'}
|
||||
label="Success"
|
||||
class="whitespace-nowrap"
|
||||
icon={CheckCircle2}
|
||||
iconProps={{ color: success === 'success' ? 'green' : 'gray' }}
|
||||
/>
|
||||
<ToggleButton
|
||||
value={'failure'}
|
||||
label="Failure"
|
||||
class="whitespace-nowrap"
|
||||
icon={AlertCircle}
|
||||
iconProps={{ color: success === 'failure' ? 'red' : 'gray' }}
|
||||
/>
|
||||
<ToggleButton value={'running'} label="Running" class="whitespace-nowrap" />
|
||||
<ToggleButton value={'success'} label="Success" class="whitespace-nowrap" />
|
||||
<ToggleButton value={'failure'} label="Failure" class="whitespace-nowrap" />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="relative w-32">
|
||||
<span class="text-xs absolute -top-4"> Show Skipped Flows </span>
|
||||
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
<Toggle size="xs" bind:checked={isSkipped} />
|
||||
<Tooltip>Skipped flows are flows that did an early break</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-6 bg-surface"
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true} startIcon={{ icon: Filter }}>
|
||||
More filters
|
||||
<Button
|
||||
color="light"
|
||||
size="xs"
|
||||
nonCaptureEvent={true}
|
||||
variant="border"
|
||||
startIcon={{ icon: Filter }}
|
||||
>
|
||||
Filter by args/result
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
|
||||
<Section label="Filters">
|
||||
<svelte:fragment slot="action">
|
||||
<CloseButton on:close={() => close(null)} />
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="w-80 flex flex-col gap-4">
|
||||
{#if mobile}
|
||||
<Label label="Filter by">
|
||||
<ToggleButtonGroup
|
||||
bind:selected={filterBy}
|
||||
on:selected={() => {
|
||||
if (!manuallySet) {
|
||||
path = null
|
||||
user = null
|
||||
folder = null
|
||||
} else {
|
||||
manuallySet = false
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleButton value="path" label="Path" />
|
||||
<ToggleButton value="user" label="User" />
|
||||
<ToggleButton value="folder" label="Folder" />
|
||||
</ToggleButtonGroup>
|
||||
</Label>
|
||||
|
||||
{#if filterBy == 'user'}
|
||||
{#key user}
|
||||
<Label label="User">
|
||||
<div class="relative w-full">
|
||||
{#if user}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
user = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<AutoComplete
|
||||
items={usernames}
|
||||
value={user}
|
||||
bind:selectedItem={user}
|
||||
inputClassName="!h-[32px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={user ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
</Label>
|
||||
{/key}
|
||||
{:else if filterBy == 'folder'}
|
||||
{#key folder}
|
||||
<Label label="Folder">
|
||||
<div class="relative w-full">
|
||||
{#if folder}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
folder = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<AutoComplete
|
||||
items={folders}
|
||||
value={folder}
|
||||
bind:selectedItem={folder}
|
||||
inputClassName="!h-[32px] py-1 !text-xs !w-64"
|
||||
hideArrow
|
||||
className={folder ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-64 !max-w-64"
|
||||
/>
|
||||
</div>
|
||||
</Label>
|
||||
{/key}
|
||||
{:else if filterBy === 'path'}
|
||||
{#key path}
|
||||
<Label label="Path">
|
||||
<div class="relative w-full">
|
||||
{#if path}
|
||||
<button
|
||||
class="absolute top-2 right-2 z-50"
|
||||
on:click={() => {
|
||||
path = null
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{:else}
|
||||
<ChevronDown class="absolute top-2 right-2" size={14} />
|
||||
{/if}
|
||||
|
||||
<AutoComplete
|
||||
items={paths}
|
||||
value={path}
|
||||
bind:selectedItem={path}
|
||||
inputClassName="!h-[32px] py-1 !text-xs !w-80"
|
||||
hideArrow
|
||||
className={path ? '!font-bold' : ''}
|
||||
dropdownClassName="!font-normal !w-80 !max-w-80"
|
||||
/>
|
||||
</div>
|
||||
</Label>
|
||||
{/key}
|
||||
{/if}
|
||||
|
||||
<Label label="Kind">
|
||||
<ToggleButtonGroup bind:selected={jobKindsCat}>
|
||||
<ToggleButton value="all" label="All" />
|
||||
<ToggleButton
|
||||
value="runs"
|
||||
label="Runs"
|
||||
showTooltipIcon
|
||||
tooltip="Runs are jobs that have no parent jobs (flows are jobs that are parent of the jobs they start), they have been triggered through the UI, a schedule or webhook"
|
||||
/>
|
||||
<ToggleButton
|
||||
value="previews"
|
||||
label="Previews"
|
||||
showTooltipIcon
|
||||
tooltip="Previews are jobs that have been started in the editor as 'Tests'"
|
||||
/>
|
||||
<ToggleButton
|
||||
value="dependencies"
|
||||
label="Deps"
|
||||
showTooltipIcon
|
||||
tooltip="Deploying a script, flow or an app launch a dependency job that create and then attach the lockfile to the deployed item. This mechanism ensure that logic is always executed with the exact same direct and indirect dependencies."
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</Label>
|
||||
|
||||
<Label label="Status">
|
||||
<ToggleButtonGroup bind:selected={success}>
|
||||
<ToggleButton value={undefined} label="All" />
|
||||
<ToggleButton value={'running'} label="Running" class="whitespace-nowrap" />
|
||||
<ToggleButton value={'success'} label="Success" class="whitespace-nowrap" />
|
||||
<ToggleButton value={'failure'} label="Failure" class="whitespace-nowrap" />
|
||||
</ToggleButtonGroup>
|
||||
</Label>
|
||||
{/if}
|
||||
|
||||
<Label label="Show Skipped Flows">
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
<Toggle size="xs" bind:checked={isSkipped} />
|
||||
<Tooltip>Skipped flows are flows that did an early break</Tooltip>
|
||||
</div>
|
||||
</Label>
|
||||
|
||||
<Label label="Hide future scheduled jobs from schedules">
|
||||
<Toggle size="xs" bind:checked={hideSchedules} />
|
||||
</Label>
|
||||
|
||||
<span class="text-xs leading-6">
|
||||
{`Filter by a json being a subset of the args/result. Try '\{"foo": "bar"\}'`}
|
||||
</span>
|
||||
<Label label="Filter by args">
|
||||
<JsonEditor on:change bind:error={argError} bind:code={copyArgFilter} />
|
||||
</Label>
|
||||
<Label label="Filter by result">
|
||||
<JsonEditor on:change bind:error={resultError} bind:code={copyResultFilter} />
|
||||
</Label>
|
||||
|
||||
<div class="flex flex-row gap-2 justify-between">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
on:click={() => {
|
||||
argFilter = ''
|
||||
resultFilter = ''
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
on:click={() => {
|
||||
argFilter = copyArgFilter
|
||||
resultFilter = copyResultFilter
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Set
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col w-72 p-2 gap-2">
|
||||
<div class="flex flex-row w-full items-center justify-between">
|
||||
<span class="text-sm eading-6 font-semibold">Filters</span>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
on:click={() => {
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<span class="text-xs leading-6">
|
||||
{`Filter by a json being a subset of the args/result. Try '\{"foo": "bar"\}'`}
|
||||
</span>
|
||||
<span class="text-xs eading-6 font-semibold">Filter by args</span>
|
||||
<JsonEditor on:change bind:error={argError} bind:code={copyArgFilter} />
|
||||
<span class="text-xs eading-6 font-semibold">Filter by result</span>
|
||||
<JsonEditor on:change bind:error={resultError} bind:code={copyResultFilter} />
|
||||
|
||||
<div class="flex flex-row gap-2 justify-between">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
on:click={() => {
|
||||
argFilter = ''
|
||||
resultFilter = ''
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
on:click={() => {
|
||||
argFilter = copyArgFilter
|
||||
resultFilter = copyResultFilter
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Set
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</div>
|
||||
|
||||
@@ -38,8 +38,3 @@ export const SCRIPT_VIEW_WEBHOOK_INFO_LINK = 'https://www.windmill.dev/docs/core
|
||||
export const SCRIPT_EDITOR_SHOW_EXPLORE_OTHER_SCRIPTS = true
|
||||
|
||||
export const SCRIPT_CUSTOMISE_SHOW_KIND = true
|
||||
|
||||
export const WORKER_S3_BUCKET_SYNC_SETTING = 'worker_s3_bucket_sync'
|
||||
export const CUSTOM_TAGS_SETTING = 'custom_tags'
|
||||
|
||||
export const WORKSPACE_SLACK_BOT_TOKEN_PATH = 'f/slack_bot/bot_token'
|
||||
|
||||
@@ -210,12 +210,12 @@ function argSigToJsonSchemaType(
|
||||
export async function loadSchemaFromPath(path: string, hash?: string): Promise<Schema> {
|
||||
if (path.startsWith('hub/')) {
|
||||
const { content, language, schema } = await ScriptService.getHubScriptByPath({ path })
|
||||
if (schema && 'properties' in schema) {
|
||||
return schema
|
||||
} else {
|
||||
if (language == 'deno' || language == 'nativets') {
|
||||
const newSchema = emptySchema()
|
||||
await inferArgs(language as SupportedLanguage, content ?? '', newSchema)
|
||||
await inferArgs('deno' as SupportedLanguage, content ?? '', newSchema)
|
||||
return newSchema
|
||||
} else {
|
||||
return schema ?? emptySchema()
|
||||
}
|
||||
} else if (hash) {
|
||||
const script = await ScriptService.getScriptByHash({
|
||||
|
||||
@@ -256,7 +256,6 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="sidebar"
|
||||
class={classNames(
|
||||
'hidden md:flex md:flex-col md:fixed md:inset-y-0 transition-all ease-in-out duration-200 shadow-md z-40 ',
|
||||
isCollapsed ? 'md:w-12' : 'md:w-40',
|
||||
@@ -308,7 +307,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="content"
|
||||
class={classNames(
|
||||
'w-full flex flex-col flex-1 h-full',
|
||||
devOnly ? '!pl-0' : isCollapsed ? 'md:pl-12' : 'md:pl-40',
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
noBackend={false}
|
||||
/>
|
||||
{#if can_write}
|
||||
<div id="app-edit-btn" class="absolute bottom-4 z-50 right-4">
|
||||
<div class="absolute bottom-4 z-50 right-4">
|
||||
<Button size="sm" startIcon={{ icon: Pen }} variant="border" href="/apps/edit/{app.path}"
|
||||
>Edit</Button
|
||||
>
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import RunsFilter from '$lib/components/runs/RunsFilter.svelte'
|
||||
import MobileFilters from '$lib/components/runs/MobileFilters.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
|
||||
import { tweened, type Tweened } from 'svelte/motion'
|
||||
import { goto } from '$app/navigation'
|
||||
import RunsQueue from '$lib/components/runs/RunsQueue.svelte'
|
||||
import { forLater } from '$lib/forLater'
|
||||
|
||||
let jobs: Job[] | undefined
|
||||
let intervalId: NodeJS.Timeout | undefined
|
||||
@@ -50,11 +50,6 @@
|
||||
? $page.url.searchParams.get('is_skipped') == 'true'
|
||||
: false
|
||||
|
||||
let hideSchedules: boolean | undefined =
|
||||
$page.url.searchParams.get('hide_scheduled') != undefined
|
||||
? $page.url.searchParams.get('hide_scheduled') == 'true'
|
||||
: false
|
||||
|
||||
let argFilter: any = $page.url.searchParams.get('arg')
|
||||
? JSON.parse(decodeURIComponent($page.url.searchParams.get('arg') ?? '{}'))
|
||||
: undefined
|
||||
@@ -84,10 +79,6 @@
|
||||
searchParams.set('is_skipped', isSkipped.toString())
|
||||
}
|
||||
|
||||
if (hideSchedules) {
|
||||
searchParams.set('hide_scheduled', hideSchedules.toString())
|
||||
}
|
||||
|
||||
// ArgFilter is an object. Encode it to a string
|
||||
argFilter && searchParams.set('arg', encodeURIComponent(JSON.stringify(argFilter)))
|
||||
resultFilter && searchParams.set('result', encodeURIComponent(JSON.stringify(resultFilter)))
|
||||
@@ -121,7 +112,7 @@
|
||||
}
|
||||
|
||||
$: ($workspaceStore && loadJobs()) ||
|
||||
(path && success && isSkipped && jobKinds && user && folder && minTs && maxTs && hideSchedules)
|
||||
(path && success && isSkipped && jobKinds && user && folder && minTs && maxTs)
|
||||
|
||||
async function fetchJobs(
|
||||
startedBefore: string | undefined,
|
||||
@@ -155,14 +146,7 @@
|
||||
getCount()
|
||||
try {
|
||||
jobs = await fetchJobs(maxTs, minTs)
|
||||
|
||||
computeCompletedJobs()
|
||||
|
||||
if (hideSchedules && !schedulePath) {
|
||||
jobs = jobs.filter(
|
||||
(job) => !(job && 'running' in job && job.scheduled_for && forLater(job.scheduled_for))
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
sendUserToast(`There was a problem fetching jobs: ${err}`, true)
|
||||
console.error(JSON.stringify(err))
|
||||
@@ -212,13 +196,6 @@
|
||||
.forEach((x) => (jobs![jobs?.findIndex((y) => y.id == x.id)!] = x))
|
||||
jobs = jobs
|
||||
computeCompletedJobs()
|
||||
|
||||
if (hideSchedules && !schedulePath) {
|
||||
jobs = jobs.filter(
|
||||
(job) =>
|
||||
!(job && 'running' in job && job.scheduled_for && forLater(job.scheduled_for))
|
||||
)
|
||||
}
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
@@ -242,11 +219,6 @@
|
||||
? $page.url.searchParams.get('is_skipped') == 'true'
|
||||
: false
|
||||
|
||||
hideSchedules =
|
||||
$page.url.searchParams.get('hide_scheduled') != undefined
|
||||
? $page.url.searchParams.get('hide_scheduled') == 'true'
|
||||
: false
|
||||
|
||||
argFilter = $page.url.searchParams.get('arg')
|
||||
? JSON.parse(decodeURIComponent($page.url.searchParams.get('arg') ?? '{}'))
|
||||
: undefined
|
||||
@@ -429,7 +401,7 @@
|
||||
|
||||
<svelte:window bind:innerWidth />
|
||||
|
||||
{#if innerWidth > 1280}
|
||||
{#if innerWidth > 768}
|
||||
<div class="w-full h-screen">
|
||||
<div class="px-2">
|
||||
<div class="flex items-center space-x-2 flex-row justify-between">
|
||||
@@ -443,23 +415,47 @@
|
||||
your own runs or runs of groups you belong to unless you are an admin.
|
||||
</Tooltip>
|
||||
</div>
|
||||
<RunsFilter
|
||||
bind:isSkipped
|
||||
bind:user
|
||||
bind:folder
|
||||
bind:path
|
||||
bind:success
|
||||
bind:argFilter
|
||||
bind:resultFilter
|
||||
bind:argError
|
||||
bind:resultError
|
||||
bind:jobKindsCat
|
||||
bind:hideSchedules
|
||||
on:change={reloadLogsWithoutFilterError}
|
||||
{usernames}
|
||||
{folders}
|
||||
{paths}
|
||||
/>
|
||||
<div class="hidden xl:block">
|
||||
<RunsFilter
|
||||
bind:isSkipped
|
||||
bind:user
|
||||
bind:folder
|
||||
bind:path
|
||||
bind:success
|
||||
bind:argFilter
|
||||
bind:resultFilter
|
||||
bind:argError
|
||||
bind:resultError
|
||||
bind:jobKindsCat
|
||||
on:change={reloadLogsWithoutFilterError}
|
||||
{usernames}
|
||||
{folders}
|
||||
{paths}
|
||||
/>
|
||||
</div>
|
||||
<div class="xl:hidden">
|
||||
<MobileFilters>
|
||||
<svelte:fragment slot="filters">
|
||||
<span class="text-xs font-semibold leading-6">Filters</span>
|
||||
<RunsFilter
|
||||
bind:isSkipped
|
||||
{paths}
|
||||
{usernames}
|
||||
{folders}
|
||||
bind:jobKindsCat
|
||||
bind:folder
|
||||
bind:path
|
||||
bind:user
|
||||
bind:success
|
||||
bind:argFilter
|
||||
bind:resultFilter
|
||||
bind:argError
|
||||
bind:resultError
|
||||
on:change={reloadLogsWithoutFilterError}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</MobileFilters>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -630,24 +626,47 @@
|
||||
your own runs or runs of groups you belong to unless you are an admin.
|
||||
</Tooltip>
|
||||
</div>
|
||||
<RunsFilter
|
||||
bind:isSkipped
|
||||
{paths}
|
||||
{usernames}
|
||||
{folders}
|
||||
bind:jobKindsCat
|
||||
bind:folder
|
||||
bind:path
|
||||
bind:user
|
||||
bind:success
|
||||
bind:argFilter
|
||||
bind:resultFilter
|
||||
bind:argError
|
||||
bind:resultError
|
||||
bind:hideSchedules
|
||||
mobile={true}
|
||||
on:change={reloadLogsWithoutFilterError}
|
||||
/>
|
||||
<div class="hidden 2xl:block">
|
||||
<RunsFilter
|
||||
bind:isSkipped
|
||||
bind:user
|
||||
bind:folder
|
||||
bind:path
|
||||
bind:success
|
||||
bind:argFilter
|
||||
bind:resultFilter
|
||||
bind:argError
|
||||
bind:resultError
|
||||
bind:jobKindsCat
|
||||
on:change={reloadLogsWithoutFilterError}
|
||||
{usernames}
|
||||
{folders}
|
||||
{paths}
|
||||
/>
|
||||
</div>
|
||||
<div class="block 2xl:hidden">
|
||||
<MobileFilters>
|
||||
<svelte:fragment slot="filters">
|
||||
<span class="text-xs font-semibold leading-6">Filters</span>
|
||||
<RunsFilter
|
||||
bind:isSkipped
|
||||
{paths}
|
||||
{usernames}
|
||||
{folders}
|
||||
bind:jobKindsCat
|
||||
bind:folder
|
||||
bind:path
|
||||
bind:user
|
||||
bind:success
|
||||
bind:argFilter
|
||||
bind:resultFilter
|
||||
bind:argError
|
||||
bind:resultError
|
||||
on:change={reloadLogsWithoutFilterError}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</MobileFilters>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2 w-full">
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import WorkspaceGroup from '$lib/components/WorkspaceGroup.svelte'
|
||||
import { CUSTOM_TAGS_SETTING, WORKER_S3_BUCKET_SYNC_SETTING } from '$lib/consts'
|
||||
import { WorkerService, type WorkerPing, SettingService, ConfigService } from '$lib/gen'
|
||||
import { enterpriseLicense, superadmin } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
@@ -37,6 +36,9 @@
|
||||
(x) => x[0]?.split(splitter)?.[0]
|
||||
)
|
||||
|
||||
const WORKER_S3_BUCKET_SYNC_SETTING = 'worker_s3_bucket_sync'
|
||||
const CUSTOM_TAGS_SETTING = 'custom_tags'
|
||||
|
||||
let timeSinceLastPing = 0
|
||||
|
||||
async function loadWorkers(): Promise<void> {
|
||||
|
||||
@@ -792,11 +792,11 @@ const config = {
|
||||
backgroundColor: lightTheme.border + ' !important',
|
||||
margin: '0 !important',
|
||||
border: 'none !important',
|
||||
zIndex: '1001 !important',
|
||||
'&::after': {
|
||||
backgroundColor: lightTheme.border + ' !important',
|
||||
margin: '0 !important',
|
||||
transform: 'none !important',
|
||||
zIndex: '1001 !important',
|
||||
transition: 'opacity 200ms !important',
|
||||
opacity: '0',
|
||||
'--splitter-hover-size': '5px',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.217.0"
|
||||
wmill_pg = ">=1.217.0"
|
||||
wmill = ">=1.216.0"
|
||||
wmill_pg = ">=1.216.0"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.217.0
|
||||
version: 1.216.0
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
BIN
python-client/wmill/wmill/AppPreview.23783724.js
Normal file
BIN
python-client/wmill/wmill/AppPreview.23783724.js
Normal file
Binary file not shown.
BIN
python-client/wmill/wmill/AppPreview.23783724.js.1
Normal file
BIN
python-client/wmill/wmill/AppPreview.23783724.js.1
Normal file
Binary file not shown.
@@ -577,11 +577,11 @@ def get_resource(
|
||||
|
||||
|
||||
@init_global_client
|
||||
def set_resource(*args, **kwargs) -> None:
|
||||
def set_resource(**kwargs) -> None:
|
||||
"""
|
||||
Set the resource at a given path as a string, creating it if it does not exist
|
||||
"""
|
||||
return _client.set_resource(*args, **kwargs)
|
||||
return _client.set_resource(**kwargs)
|
||||
|
||||
|
||||
@init_global_client
|
||||
@@ -592,33 +592,33 @@ def set_state(value: Any) -> None:
|
||||
return _client.set_state(value)
|
||||
|
||||
|
||||
def set_shared_state_pickle(*args, **kwargs) -> None:
|
||||
def set_shared_state_pickle(**kwargs) -> None:
|
||||
"""
|
||||
Set the state in the shared folder using pickle
|
||||
"""
|
||||
return Windmill.set_shared_state_pickle(args, **kwargs)
|
||||
return Windmill.set_shared_state_pickle(**kwargs)
|
||||
|
||||
|
||||
@deprecate("Windmill.get_shared_state_pickle(...)")
|
||||
def get_shared_state_pickle(*args, **kwargs) -> Any:
|
||||
def get_shared_state_pickle(**kwargs) -> Any:
|
||||
"""
|
||||
Get the state in the shared folder using pickle
|
||||
"""
|
||||
return Windmill.get_shared_state_pickle(*args, **kwargs)
|
||||
return Windmill.get_shared_state_pickle(**kwargs)
|
||||
|
||||
|
||||
def set_shared_state(*args, **kwargs) -> None:
|
||||
def set_shared_state(**kwargs) -> None:
|
||||
"""
|
||||
Set the state in the shared folder using pickle
|
||||
"""
|
||||
return Windmill.set_shared_state(*args, **kwargs)
|
||||
return Windmill.set_shared_state(**kwargs)
|
||||
|
||||
|
||||
def get_shared_state(*args, **kwargs) -> None:
|
||||
def get_shared_state(**kwargs) -> None:
|
||||
"""
|
||||
Set the state in the shared folder using pickle
|
||||
"""
|
||||
return Windmill.get_shared_state(*args, **kwargs)
|
||||
return Windmill.get_shared_state(**kwargs)
|
||||
|
||||
|
||||
@init_global_client
|
||||
|
||||
138
python-client/wmill/wmill/flow.py
Normal file
138
python-client/wmill/wmill/flow.py
Normal file
@@ -0,0 +1,138 @@
|
||||
import functools
|
||||
import ast
|
||||
import inspect
|
||||
import yaml
|
||||
from graphlib import TopologicalSorter
|
||||
|
||||
def flow(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
class Edge(object):
|
||||
def __init__(self, _node, _path):
|
||||
self._node = _node
|
||||
self._path = _path
|
||||
# print("DECL EDGE: node: " + str(self._node) + " path: " + str(self._path))
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return Edge(self._node, self._path + [attr])
|
||||
|
||||
def __str__(self):
|
||||
return f"EDGE: {self._node}({self._path})"
|
||||
|
||||
class Node(object):
|
||||
def __init__(self, _id, _deps, _f):
|
||||
self._id = _id
|
||||
self._deps = _deps
|
||||
self._f = _f
|
||||
# print("DECL NODE: id: " + str(self._id) + ", deps: " + str([f"{k}={v.__str__()}" for k, v in self._deps.items()]))
|
||||
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return Edge(self, [attr])
|
||||
|
||||
def __str__(self):
|
||||
k_v = [f"{k}={v.__str__()}" for k, v in self._deps.items()]
|
||||
return f"{self._id}({k_v})"
|
||||
|
||||
def step(f):
|
||||
# print("DECL F: " + f.__name__)
|
||||
# code = inspect.getsource(f)
|
||||
# print(code)
|
||||
# print(ast.dump(ast.parse(code)))
|
||||
# print(ast.unparse(ast.parse(code)))
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
kwargs.update(dict(zip(f.__code__.co_varnames, args)))
|
||||
for k, v in kwargs.items():
|
||||
if isinstance(v, Node):
|
||||
kwargs[k] = Edge(v, [])
|
||||
return Node(f.__name__, kwargs, f)
|
||||
|
||||
return wrapper
|
||||
|
||||
@step
|
||||
def d():
|
||||
return 32
|
||||
|
||||
@step
|
||||
def a(a):
|
||||
import pandas
|
||||
return 32
|
||||
|
||||
@step
|
||||
def b(b):
|
||||
import pandas
|
||||
return 32
|
||||
|
||||
@step
|
||||
def c(x, y):
|
||||
return x + y + 1
|
||||
|
||||
def flow():
|
||||
r_a = a(d())
|
||||
r_b = b(2)
|
||||
r_c = c(r_a, r_b)
|
||||
return r_c
|
||||
|
||||
def get_edges_and_nodes(graph):
|
||||
edges = {}
|
||||
nodes = [graph]
|
||||
all_nodes = []
|
||||
seen = set()
|
||||
while nodes:
|
||||
node = nodes.pop()
|
||||
id = node._id
|
||||
if id in seen:
|
||||
continue
|
||||
all_nodes.append(node)
|
||||
seen.add(id)
|
||||
for _, v in node._deps.items():
|
||||
if isinstance(v, Edge):
|
||||
if id not in edges:
|
||||
edges[id] = []
|
||||
edges[id].append(v._node._id)
|
||||
nodes.append(v._node)
|
||||
return edges, {n._id: n for n in all_nodes}
|
||||
|
||||
def render(graph):
|
||||
edges, all_nodes = get_edges_and_nodes(graph)
|
||||
# print(edges)
|
||||
# print(all_nodes)
|
||||
ts = TopologicalSorter(edges)
|
||||
sortd = tuple(ts.static_order())
|
||||
modules_f = [[id, all_nodes[id]] for id in sortd]
|
||||
files = [ n._f for n in all_nodes.values()]
|
||||
sources = gen_sources(files)
|
||||
print(sources)
|
||||
modules = [{"id": k, "lang": "python", "content": render_f(v._f)} for k, v in modules_f]
|
||||
return yaml.dump(modules)
|
||||
|
||||
def gen_module_sources(source):
|
||||
pass
|
||||
|
||||
def gen_sources(files):
|
||||
files = set(files)
|
||||
asts = []
|
||||
sources = {}
|
||||
for f in files:
|
||||
asts.append(ast.parse(inspect.getsource(f)))
|
||||
|
||||
for astf in asts:
|
||||
for node in astf.body:
|
||||
if isinstance(node, ast.FunctionDef):
|
||||
print(node.args)
|
||||
code = ast.unparse(node.body)
|
||||
sources[node.name] = code
|
||||
|
||||
return sources
|
||||
|
||||
def render_f(f):
|
||||
code = inspect.getsource(f)
|
||||
return ast.unparse(ast.parse(code))
|
||||
|
||||
rendered = render(flow())
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.217.0"
|
||||
version = "1.216.0"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.217.0",
|
||||
"version": "1.216.0",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.217.0
|
||||
1.216.0
|
||||
|
||||
Reference in New Issue
Block a user