Compare commits

...

41 Commits

Author SHA1 Message Date
Ruben Fiszel
c90d0965a5 all 2024-06-26 19:01:06 +02:00
Ruben Fiszel
2c4c443f75 Merge branch 'main' into rf/extraDocker 2024-06-26 18:36:54 +02:00
Ruben Fiszel
17c586ed8f fix: fix build 2024-06-26 18:24:47 +02:00
Ruben Fiszel
afa73ad6ea all 2024-06-26 17:50:03 +02:00
Ruben Fiszel
e38f7e6e82 chore(main): release 1.355.0 (#3980)
* chore(main): release 1.355.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-06-26 17:05:04 +02:00
Ruben Fiszel
fa2f732182 feat: add wmill cli image 2024-06-26 16:58:09 +02:00
Ruben Fiszel
75555f725c small toggle fix for list & objects 2024-06-26 16:58:09 +02:00
Faton Ramadani
b9a73c5b82 Fr/fix modal overflow (#3979)
* feat(frontend): wip

* feat(frontend): Add missing stylings

* feat(frontend): Add output panel doc links

* feat(frontend): fix popover word break

* fix(frontend): wip

* fix(frontend): wip

* fix(frontend): fix modal

* fix(frontend): remove useless class

* fix(frontend): done

* fix(frontend): remove useless code

* fix(frontend): simplify code

* fix(frontend): simplify code

* fix(frontend): Fix height

* fix(frontend): Fix height

* fix(frontend): allow overflow while dragging

* fix(frontend): revert changes
2024-06-26 16:19:37 +02:00
HugoCasa
f29c46a282 fix: remove useless metrics (#3962)
* fix: remove useless metrics

* fix: nit

* feat: update ee ref (renew key on start + vcpu usage)

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-06-26 15:28:09 +02:00
Faton Ramadani
73175acc01 feat(frontend): Add context for inner components of list, groups and … (#3974)
* feat(frontend): Add context for inner components of list, groups and tables

* feat(frontend): improve code

* feat(frontend): avoid context duplicates

* feat(frontend): recursively explore parents to find all contexts

* feat(frontend): improve code

* feat(frontend): improve naming

* feat(frontend): improve code

* feat(frontend): optimise code

* feat(frontend): optimise code
2024-06-26 15:24:00 +02:00
HugoCasa
08476a3794 fix(frontend): add tooltip to schedule pausing (#3984) 2024-06-26 15:23:10 +02:00
Ruben Fiszel
275c10b17a update tsdocs 2024-06-26 14:17:31 +02:00
HugoCasa
e9b310eee0 fix: parallel branchall disordered skip failure retrieval (#3975)
* fix: parallel branchall disordered skip failure retrieval

* fix: use regex for branchall index

* patch(frontend): default to parallel branch all
2024-06-26 12:23:09 +02:00
HugoCasa
dd804839f0 fix: hide draft only items in pickers and from ops (#3977)
* fix: hide draft only scripts to ops and in pickers/flow builder

* fix: default to hiding draft only scripts

* fix: hide script drafts from ops

* fix: generalize to hide draft only/no main func by default

* fix: archived openapi nit
2024-06-26 12:21:45 +02:00
Ruben Fiszel
9ac18c33bf update tsdocs 2024-06-26 11:45:43 +02:00
Ruben Fiszel
dc68f3e939 chore(main): release 1.354.0 (#3971) 2024-06-26 01:32:51 +02:00
Ruben Fiszel
97215a979f internal fix to improve exportability of code in react-sdk (#3978)
* all

* all

* all

* all

* fix

* remove vite config

* remove uncessary app.css

* all

* all

* all

* all

* all
2024-06-26 01:31:24 +02:00
HugoCasa
f8dc21542f feat: schedule pausing (#3976)
* feat: schedule pausing

* fix: improve UI
2024-06-25 23:39:34 +02:00
Faton Ramadani
cab248ceff feat(frontend): app editor help box (#3970)
* feat(frontend): wip

* feat(frontend): Add missing stylings

* feat(frontend): Add output panel doc links

* feat(frontend): fix popover word break

* feat(frontend): fix theme
2024-06-25 13:44:13 +02:00
Ruben Fiszel
6db033e36a chore(main): release 1.353.0 (#3959)
* chore(main): release 1.353.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-06-24 21:18:59 +02:00
Faton Ramadani
a804e013c2 fix(frontend): Add missing on change event when connecting an input (#3964) 2024-06-24 21:02:14 +02:00
Henri Courdent
189a2815a0 Type fix & helpers approval steps (#3950) 2024-06-24 15:26:20 +02:00
wendrul
4ef38233c1 fix: Improve cancel selected jobs action (#3960)
* Improve cancel selected action

* Add a `Select all` button
* Make the confirmation clearer by adding
* Fix bug where you could select uncancelable jobs throught the graph

* Switch buttons place
2024-06-24 15:24:25 +02:00
Faton Ramadani
ee7db8c8e3 feat(frontend): add an indicator when a for loop has no iterator expression (#3961) 2024-06-24 15:24:10 +02:00
Faton Ramadani
183361df85 fix(frontend): improve disable style of the select component (#3938)
* feat(frontend): improve disable style of the select component

* fix(frontend): improve inputs style

* fix(frontend): fix build

* fix(frontend): revert unnecessary changes
2024-06-24 13:20:35 +02:00
Ruben Fiszel
7c64f0a5b2 minor ui fix 2024-06-24 00:23:35 +02:00
Ruben Fiszel
2a88fcbfd8 rm docs redundance 2024-06-22 14:52:24 +02:00
Ruben Fiszel
a859705efd chore(main): release 1.352.0 (#3953)
* chore(main): release 1.352.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-06-22 14:27:02 +02:00
Ruben Fiszel
e0479e05f7 fix: improve perf of forloop with parallelism massively 2024-06-22 14:14:15 +02:00
HugoCasa
b57afc8f68 feat: add extra metadata to folders/resources/types + path prefix filtering for all + improve groups UI (#3936)
* feat: add extra metadata to folders/schedules/resources/types

* feat: add path prefix filter to api

* chore: updatee ref

* feat: improve groups UI

* fix: nit

* fix: nit
2024-06-22 01:29:23 +02:00
HugoCasa
0c661220cf feat: fallback to default hub if official script not found on private hub (#3951) 2024-06-22 01:29:08 +02:00
Ruben Fiszel
53eeef0503 fix: cache wasm 2024-06-22 00:58:49 +02:00
Ruben Fiszel
f626a361c4 chore(main): release 1.351.0 (#3948)
* chore(main): release 1.351.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-06-21 19:39:24 +02:00
Ruben Fiszel
ba76d8749a fix: improve support of relative paths in subflows and apps 2024-06-21 18:07:58 +02:00
Faton Ramadani
ff4ea86155 fix show all light theme (#3949) 2024-06-21 17:09:17 +02:00
Faton Ramadani
25a460be85 feat(frontend): improve approval form edition + add a delete button t… (#3946)
* feat(frontend): improve approval form edition + add a delete button to the EditableSchema

* feat(frontend): only show a few lines of code for Hub/workflow scripts by default

* feat(frontend): fix spacing

* feat(frontend): improve

* feat(frontend): fix toggle

* feat(frontend): add missing space
2024-06-21 16:57:09 +02:00
Ruben Fiszel
e5db2782f5 add tsdocs 2024-06-21 15:59:06 +02:00
Ruben Fiszel
b2111b7d11 chore(main): release 1.350.3 (#3947)
* chore(main): release 1.350.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-06-21 15:30:29 +02:00
Ruben Fiszel
1042a06a12 fix(cli): fix push flow support for windows 2024-06-21 15:27:16 +02:00
Ruben Fiszel
0567d74abf chore(main): release 1.350.2 (#3945)
* chore(main): release 1.350.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-06-21 14:27:34 +02:00
Ruben Fiszel
3f0cd6349d fix(cli): support windows more thoroughly + fix generate flow locks (#3944)
* all

* all

* all

* fix lock

* fix lock

* fix lock

* fix lock
2024-06-21 14:23:33 +02:00
1020 changed files with 9925 additions and 1625 deletions

55
.github/workflows/build_cli_image.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}-cli
name: Publish cli image
on:
push:
tags:
- "v*"
workflow_dispatch:
permissions:
contents: read
id-token: write
packages: write
jobs:
publish_cli:
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: depot/setup-action@v1
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Login to registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push publicly
uses: depot/build-push-action@v1
with:
file: "./docker/DockerfileCli"
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ steps.meta.outputs.tags }}
labels: |
${{ steps.meta.outputs.labels }}
org.opencontainers.image.licenses=AGPLv3

View File

@@ -17,5 +17,8 @@ jobs:
with:
toolchain: stable
- name: update lockfile
run: cd backend && cargo generate-lockfile
run: |
cd backend
cargo generate-lockfile
cargo update -p bit-vec@0.7.0 --precise 0.6.3
- uses: stefanzweifel/git-auto-commit-action@v4

View File

@@ -1,50 +0,0 @@
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
name: Build windmill:python310
on: workflow_dispatch
concurrency:
group: ${{ github.ref }}-python3.10
cancel-in-progress: true
permissions:
contents: read
id-token: write
packages: write
jobs:
build_ee:
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v2
- uses: depot/setup-action@v1
- name: Login to registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Edit python version
run: |
sed -i 's/python:3.11.4/python:3.10.12/g' Dockerfile
- name: Build and push publicly ee
uses: depot/build-push-action@v1
with:
context: .
push: true
build-args: |
features=enterprise
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:python310
labels: |
org.opencontainers.image.licenses=Windmill-Enterprise-License

View File

@@ -1,46 +0,0 @@
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
name: Build windmill:mssql
on:
workflow_dispatch:
concurrency:
group: ${{ github.ref }}-mssql
cancel-in-progress: true
permissions:
contents: read
id-token: write
packages: write
jobs:
build_ee:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: depot/setup-action@v1
- name: Login to registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push publicly ee
uses: depot/build-push-action@v1
with:
context: .
push: true
file: ./docker/DockerfileMssql
build-args: |
features=enterprise
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:mssql
labels: |
org.opencontainers.image.licenses=Windmill-Enterprise-License

View File

@@ -1,5 +1,83 @@
# Changelog
## [1.355.0](https://github.com/windmill-labs/windmill/compare/v1.354.0...v1.355.0) (2024-06-26)
### Features
* add wmill cli image ([fa2f732](https://github.com/windmill-labs/windmill/commit/fa2f732182c8ac103c3fdcb0cfe5eecba2474382))
* **frontend:** Add context for inner components of list, groups and … ([#3974](https://github.com/windmill-labs/windmill/issues/3974)) ([73175ac](https://github.com/windmill-labs/windmill/commit/73175acc018651eb0d3c161c6176e96576178813))
### Bug Fixes
* **frontend:** add tooltip to schedule pausing ([#3984](https://github.com/windmill-labs/windmill/issues/3984)) ([08476a3](https://github.com/windmill-labs/windmill/commit/08476a379456039cc8569125ca3535008d6f7bea))
* hide draft only items in pickers and from ops ([#3977](https://github.com/windmill-labs/windmill/issues/3977)) ([dd80483](https://github.com/windmill-labs/windmill/commit/dd804839f07aa56f6707fa122fa8e8bbcef81f6e))
* parallel branchall disordered skip failure retrieval ([#3975](https://github.com/windmill-labs/windmill/issues/3975)) ([e9b310e](https://github.com/windmill-labs/windmill/commit/e9b310eee0d73f3518c85b572e05473c449959a0))
* remove useless metrics ([#3962](https://github.com/windmill-labs/windmill/issues/3962)) ([f29c46a](https://github.com/windmill-labs/windmill/commit/f29c46a282e7cb1a9f093a3089e4eefd24a7bc1e))
## [1.354.0](https://github.com/windmill-labs/windmill/compare/v1.353.0...v1.354.0) (2024-06-25)
### Features
* **frontend:** app editor help box ([#3970](https://github.com/windmill-labs/windmill/issues/3970)) ([cab248c](https://github.com/windmill-labs/windmill/commit/cab248cefff73bab818c477b4e40220b3d81eac9))
* schedule pausing ([#3976](https://github.com/windmill-labs/windmill/issues/3976)) ([f8dc215](https://github.com/windmill-labs/windmill/commit/f8dc21542fd3a57e4272d874a034a3af1cdd8348))
## [1.353.0](https://github.com/windmill-labs/windmill/compare/v1.352.0...v1.353.0) (2024-06-24)
### Features
* **frontend:** add an indicator when a for loop has no iterator expression ([#3961](https://github.com/windmill-labs/windmill/issues/3961)) ([ee7db8c](https://github.com/windmill-labs/windmill/commit/ee7db8c8e33eb990279d5badc95dc54b0c6124dd))
### Bug Fixes
* **frontend:** Add missing on change event when connecting an input ([#3964](https://github.com/windmill-labs/windmill/issues/3964)) ([a804e01](https://github.com/windmill-labs/windmill/commit/a804e013c2672478469edd5e512d7ce63ba92bcd))
* **frontend:** improve disable style of the select component ([#3938](https://github.com/windmill-labs/windmill/issues/3938)) ([183361d](https://github.com/windmill-labs/windmill/commit/183361df85e5efbcea4a53bddc3e641cbf73acd2))
* Improve cancel selected jobs action ([#3960](https://github.com/windmill-labs/windmill/issues/3960)) ([4ef3823](https://github.com/windmill-labs/windmill/commit/4ef38233c1a21b143456fe79d5675b2ad8f3606e))
## [1.352.0](https://github.com/windmill-labs/windmill/compare/v1.351.0...v1.352.0) (2024-06-22)
### Features
* add extra metadata to folders/resources/types + path prefix filtering for all + improve groups UI ([#3936](https://github.com/windmill-labs/windmill/issues/3936)) ([b57afc8](https://github.com/windmill-labs/windmill/commit/b57afc8f68f691ad5ef7fb16aafe6fae7d1138e9))
* fallback to default hub if official script not found on private hub ([#3951](https://github.com/windmill-labs/windmill/issues/3951)) ([0c66122](https://github.com/windmill-labs/windmill/commit/0c661220cf1fb7eb37ad7f39b1fa6d29570edda2))
### Bug Fixes
* cache wasm ([53eeef0](https://github.com/windmill-labs/windmill/commit/53eeef05032f04637249c2557d6d864f9446a9a1))
* improve perf of forloop with parallelism massively ([e0479e0](https://github.com/windmill-labs/windmill/commit/e0479e05f7b1a212906afd8c5e268f3d48266311))
## [1.351.0](https://github.com/windmill-labs/windmill/compare/v1.350.3...v1.351.0) (2024-06-21)
### Features
* **frontend:** improve approval form edition + add a delete button t… ([#3946](https://github.com/windmill-labs/windmill/issues/3946)) ([25a460b](https://github.com/windmill-labs/windmill/commit/25a460be858ce020c40cd120e5aef2a79ba362d6))
### Bug Fixes
* improve support of relative paths in subflows and apps ([ba76d87](https://github.com/windmill-labs/windmill/commit/ba76d8749a9524ed356611c09f874da60f3b954a))
## [1.350.3](https://github.com/windmill-labs/windmill/compare/v1.350.2...v1.350.3) (2024-06-21)
### Bug Fixes
* **cli:** fix push flow support for windows ([1042a06](https://github.com/windmill-labs/windmill/commit/1042a06a12ae31d31facd1dfab10cbd2b10c86f5))
## [1.350.2](https://github.com/windmill-labs/windmill/compare/v1.350.1...v1.350.2) (2024-06-21)
### Bug Fixes
* **cli:** support windows more thoroughly + fix generate flow locks ([#3944](https://github.com/windmill-labs/windmill/issues/3944)) ([3f0cd63](https://github.com/windmill-labs/windmill/commit/3f0cd6349de37358f3942772254e2509383305b4))
## [1.350.1](https://github.com/windmill-labs/windmill/compare/v1.350.0...v1.350.1) (2024-06-20)

View File

@@ -23,6 +23,8 @@ ENV SQLX_OFFLINE=true
FROM node:20-alpine as frontend
ARG WITH_DENO=true
# install dependencies
WORKDIR /frontend
COPY ./frontend/package.json ./frontend/package-lock.json ./
@@ -37,6 +39,7 @@ COPY /backend/windmill-api/build_openapi.sh /backend/windmill-api/build_openapi.
RUN cd /backend/windmill-api && . ./build_openapi.sh
COPY /backend/parsers/windmill-parser-wasm/pkg/ /backend/parsers/windmill-parser-wasm/pkg/
COPY /typescript-client/docs/ /frontend/static/tsdocs/
RUN npm run generate-backend-client
ENV NODE_OPTIONS "--max-old-space-size=8192"
@@ -84,12 +87,17 @@ SHELL ["/bin/bash", "-c"]
RUN apt update -y
RUN apt install -y unzip curl
RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.44.1/deno-x86_64-unknown-linux-gnu.zip -o deno.zip || true
RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.44.1/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
RUN unzip deno.zip && rm deno.zip
ARG BUILD_ENV=copy
# FROM alpine as build_copy
# ONBUILD COPY file /file
# FROM alpine as build_no_copy
# ONBUILD RUN echo "I don't copy"
FROM ${PYTHON_IMAGE}
ARG TARGETPLATFORM
@@ -101,6 +109,10 @@ ARG APP=/usr/src/app
ARG WITH_POWERSHELL=true
ARG WITH_KUBECTL=true
ARG WITH_HELM=true
ARG WITH_GO=true
ARG WITH_BUN=true
ARG WITH_PYTHON=true
RUN apt-get update \
@@ -136,7 +148,8 @@ RUN if [ "$WITH_KUBECTL" = "true" ]; then \
else echo 'Building the image without kubectl'; fi
RUN set -eux; \
RUN if [ "$WITH_GO" = "true" ]; then \
set -eux; \
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
case "$arch" in \
'amd64') \
@@ -150,7 +163,8 @@ RUN set -eux; \
;; \
*) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \
esac; \
wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz";
wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz"; \
else echo 'Building the image without go'; fi
ENV PATH="${PATH}:/usr/local/go/bin"
ENV GO_PATH=/usr/local/go/bin/go
@@ -159,7 +173,9 @@ RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get -y update && apt-get install -y curl nodejs awscli
# go build is slower the first time it is ran, so we prewarm it in the build
RUN mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm && printf "package foo\nimport (\"fmt\")\nfunc main() { fmt.Println(42) }" > warm.go && go mod tidy && go build -x && rm -rf /tmp/gobuildwarm
RUN if [ "$WITH_GO" = "true" ]; then \
mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm && printf "package foo\nimport (\"fmt\")\nfunc main() { fmt.Println(42) }" > warm.go && go mod tidy && go build -x && rm -rf /tmp/gobuildwarm \
else echo 'Building the image without go'; fi
ENV TZ=Etc/UTC

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT created_at FROM metrics WHERE id = 'author_count' ORDER BY created_at DESC LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "created_at",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": []
},
"nullable": [
false
]
},
"hash": "021be0f26ea87e587e656b24a9a94538efbf54a1447a3898e19773789cfc9063"
}

View File

@@ -22,6 +22,16 @@
"ordinal": 3,
"name": "description",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "created_by",
"type_info": "Varchar"
}
],
"parameters": {
@@ -34,6 +44,8 @@
false,
false,
true,
true,
true,
true
]
},

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue SET suspend = suspend - 1 WHERE id = $1 AND suspend > 0",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": []
},
"hash": "0c349ac832cbf055fce755c8e2081eacf2b836b0396ec48a6cc2405fb7aa76de"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO metrics (id, value) VALUES ('worker_usage', $1)",
"query": "INSERT INTO metrics (id, value) VALUES ('telemetry', $1)",
"describe": {
"columns": [],
"parameters": {
@@ -10,5 +10,5 @@
},
"nullable": []
},
"hash": "5ce9a9e0669b299b998bc9080d24f8d6f56eb049bec2e0745d27ca3a4fc0c296"
"hash": "0cb84cbb9083d967cc8be1cccab5be61080c1003eef51eea41862b25c2b93de6"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET last_ping = now()\n WHERE id = $1 AND last_ping < now()",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": []
},
"hash": "262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM metrics \n WHERE (id = 'author_count' OR id = 'operator_count' OR id = 'worker_usage') AND created_at < NOW() - INTERVAL '6 month'",
"describe": {
"columns": [],
"parameters": {
"Left": []
},
"nullable": []
},
"hash": "3667d72d23e8c35ab96d5e5d1fbfd94a8a9f74b6398e21f13c206c3f9427c6a9"
}

View File

@@ -35,31 +35,41 @@
},
{
"ordinal": 6,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "created_by",
"type_info": "Varchar"
},
{
"ordinal": 8,
"name": "is_expired",
"type_info": "Bool"
},
{
"ordinal": 7,
"ordinal": 9,
"name": "is_refreshed",
"type_info": "Bool"
},
{
"ordinal": 8,
"ordinal": 10,
"name": "refresh_error",
"type_info": "Text"
},
{
"ordinal": 9,
"ordinal": 11,
"name": "is_linked",
"type_info": "Bool"
},
{
"ordinal": 10,
"ordinal": 12,
"name": "is_oauth?",
"type_info": "Bool"
},
{
"ordinal": 11,
"ordinal": 13,
"name": "account",
"type_info": "Int4"
}
@@ -77,6 +87,8 @@
true,
false,
false,
true,
true,
null,
null,
true,

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3",
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3, edited_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -9,10 +9,11 @@
"Varchar",
"Jsonb",
"Text",
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "489a62b5943a7a21ce487aa7b72a63dfc6300dd93bc29f5ec4cb1bfc471ad0bf"
"hash": "42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4"
}

View File

@@ -130,6 +130,11 @@
},
{
"ordinal": 25,
"name": "paused_until",
"type_info": "Timestamptz"
},
{
"ordinal": 26,
"name": "jobs",
"type_info": "JsonArray"
}
@@ -167,6 +172,7 @@
true,
false,
true,
true,
null
]
},

View File

@@ -32,6 +32,16 @@
"ordinal": 5,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 6,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "created_by",
"type_info": "Varchar"
}
],
"parameters": {
@@ -45,7 +55,9 @@
true,
true,
false,
false
false,
true,
true
]
},
"hash": "45e4d13f5806122faecdb1d9ab18159555b652869a036b006f4a151e999b17b7"

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "584cb984ea6528baac48c5c437ad2ee3bef92e3fada73dcf519147964c0f4f4a"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE resource SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "5eb1e072a6cc6ace25a5806b5adfdc0ae5c89c556daf491f39e629951a912c6c"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, $2, $3, $4, $5)",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, now())",
"describe": {
"columns": [],
"parameters": {
@@ -9,10 +9,12 @@
"Varchar",
"Varchar",
"VarcharArray",
"Jsonb"
"Jsonb",
"Text",
"Varchar"
]
},
"nullable": []
},
"hash": "e33d0bec7e08c0bb4e6f26c93a636707714699e8984b9f537085e773849c8b43"
"hash": "5f3f1f1ca72b0392f227d22adef9ef795049e60a823e8ce754a3f9d91ef1b6bb"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "6e6d781fe63602541f7cb4806d5ddde0792e1861ea8761b4875677c3d0594a86"
}

View File

@@ -1,18 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, description = $4, resource_type = $5",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Jsonb",
"Text",
"Varchar"
]
},
"nullable": []
},
"hash": "726f77c2c1ef63eece7a1176bcbb916987fc29b04b889544a064dc7cd5eab36f"
}

View File

@@ -22,6 +22,16 @@
"ordinal": 3,
"name": "description",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "created_by",
"type_info": "Varchar"
}
],
"parameters": {
@@ -33,6 +43,8 @@
false,
false,
true,
true,
true,
true
]
},

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "7ded28aae42230da6e3aa6fa952268e8e15cc8b16770c20ec6be2a9e82ab93c9"
}

View File

@@ -0,0 +1,44 @@
{
"db_name": "PostgreSQL",
"query": "SELECT worker, worker_instance, vcpus, ping_at, started_at FROM worker_ping WHERE ping_at > now() - interval '30 days'",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "worker",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "worker_instance",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "vcpus",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "ping_at",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "started_at",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
false,
true,
false,
false
]
},
"hash": "8480792eefdd0f31a49afec0f91262a6973c068da5413b154eff9b9da90db95f"
}

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "98c8e2c58b38beedcb474251c2554a2f1b768ec7ed1a419313f4842f26425e5d"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
"query": "SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
"describe": {
"columns": [
{
@@ -27,6 +27,21 @@
"ordinal": 4,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 5,
"name": "summary",
"type_info": "Text"
},
{
"ordinal": 6,
"name": "created_by",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_at",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -41,8 +56,11 @@
false,
false,
false,
false
false,
true,
true,
true
]
},
"hash": "8543f029d9784234e4c6a6dcd7b03e62d544b98be261334ee210594e0bb839f2"
"hash": "9c6d44ffae63b4050ef3a66cb05d5a4ee50dea4b30093df2e0ceb684201ca4b0"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder\n (workspace_id, name, display_name, owners, extra_perms)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING",
"query": "INSERT INTO folder\n (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
@@ -9,10 +9,11 @@
"Varchar",
"Varchar",
"VarcharArray",
"Jsonb"
"Jsonb",
"Varchar"
]
},
"nullable": []
},
"hash": "f2aee3ae39c90e40dd1835befc339e5381cc9104933cf8e90d840a9bf638ff52"
"hash": "ad3b0176e3d41a0c1d79a8eb59da2a9cc4fee3473b3f6ea69f397d91e9356ba1"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "b7ac3383a058ab3bbd7280300cebc5f81eeb9e96622ad2557f810a8b91641b89"
}

View File

@@ -22,6 +22,16 @@
"ordinal": 3,
"name": "description",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "created_by",
"type_info": "Varchar"
}
],
"parameters": {
@@ -33,6 +43,8 @@
false,
false,
true,
true,
true,
true
]
},

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT id FROM queue WHERE parent_job = $1 AND suspend > 0 ORDER by suspend",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
false
]
},
"hash": "b8d97d300ffe6fae99f2396ae07cef03903752d17ce3fdadca47d86da75139aa"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE resource SET path = $1, value = $2 WHERE path = $3 AND workspace_id = $4",
"query": "UPDATE resource SET path = $1, value = $2, edited_at = now() WHERE path = $3 AND workspace_id = $4",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
"hash": "8c0131a9cc61f2daa258d49767242bcaab6bb34a977ff7fb0c18aa9202d11f47"
"hash": "be10033b37dc9cbc38cc56afa8ef93cd7dbe267525d78cbfea87bb7c7cbf8ec4"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE folder SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "c2a6e8f38d2293c45ede26b89a976f1a32f5bc2323a28f640cf3301a299f1cf4"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO resource\n (workspace_id, path, value, resource_type)\n VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3",
"query": "INSERT INTO resource\n (workspace_id, path, value, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, edited_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -8,10 +8,11 @@
"Varchar",
"Varchar",
"Jsonb",
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "ad9ccde8d831461f1f312d530867714577a825cd40d6655ed61b0b343b5d4482"
"hash": "c4ee16065fa021cf6443c2f29f1ca986b9730eae9df26dd33c3ea21e35df62c4"
}

View File

@@ -0,0 +1,19 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Jsonb",
"Text",
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO metrics (id, value) VALUES ('author_count', $1), ('operator_count', $2)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Jsonb"
]
},
"nullable": []
},
"hash": "c6b2791dd109c7bf40a40b557d9d5a140e70c49718fcb3a776b77f8f2ab901d4"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE name = $1 AND workspace_id = $2",
"query": "SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE name = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
@@ -27,6 +27,21 @@
"ordinal": 4,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 5,
"name": "summary",
"type_info": "Text"
},
{
"ordinal": 6,
"name": "created_by",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_at",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -40,8 +55,11 @@
false,
false,
false,
false
false,
true,
true,
true
]
},
"hash": "3911bf3bbc82d87366a5297496fd2350a252bcd69f1c7c972bf566ee7eb28b0a"
"hash": "cb20f04352364f112ec564617722354577dc5f77169fd4573317454e47361d65"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO resource (workspace_id, path, value, description, resource_type, created_by, edited_at) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme', $2, now()) ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "cccf9e216d84dfbb801b92d697496f44d93b344190a26c8393867b3a268b9aba"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE resource_type SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "d1b508e1d8496e5e7aee9db555f9740128d87fe5183f9b7b72f550a5502e1cbb"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description)\n VALUES ($1, $2, $3, $4)",
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now())",
"describe": {
"columns": [],
"parameters": {
@@ -8,10 +8,11 @@
"Varchar",
"Varchar",
"Jsonb",
"Text"
"Text",
"Varchar"
]
},
"nullable": []
},
"hash": "5061c0d054bf4f028e7fe51a8f9389024c6ae4492755cadac0f7167e5300bda0"
"hash": "d249343a1d9fce2f7ca9e6e447eb32d7d11b61acdab80dda24db4a9ba3e1f7e6"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Int4"
]
},
"nullable": []
},
"hash": "d323e898931b200eeec8c5608c37043ed1c47b6f6a53dbf562ad09ece29e82e4"
}

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO resource (workspace_id, path, value, description, resource_type) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme') ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "d956b7525f83e6d03beadc4bb3ee2798f53d990b01b17bdbc044719d4908e3f4"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT script_path FROM completed_job WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "script_path",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true
]
},
"hash": "e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb"
}

View File

@@ -22,6 +22,16 @@
"ordinal": 3,
"name": "description",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "created_by",
"type_info": "Varchar"
}
],
"parameters": {
@@ -31,6 +41,8 @@
false,
false,
true,
true,
true,
true
]
},

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "f581f5ad5c386cef1037e3e8dcb759164e21a72ddc859304421ccb96233fa277"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE schedule SET paused_until = NULL WHERE workspace_id = $1 AND path = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": []
},
"hash": "f9fed1aa8a4bbf1749da8c91b9cbae5605cd3ae7fd2e7ee774411971fa1ddc01"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE resource SET value = $1 WHERE path = $2 AND workspace_id = $3",
"query": "UPDATE resource SET value = $1, edited_at = now() WHERE path = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
@@ -12,5 +12,5 @@
},
"nullable": []
},
"hash": "8876fa929ffb175cd976a2bca1195704aa9fe7215013ae29e49ef15cb201ba57"
"hash": "fd790067c02d384aead5ff8f94efb37cc20d2f7a3749d21a1de5642e888b5f42"
}

430
backend/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.350.1"
version = "1.355.0"
authors.workspace = true
edition.workspace = true
@@ -24,7 +24,7 @@ members = [
]
[workspace.package]
version = "1.350.1"
version = "1.355.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -159,19 +159,19 @@ tokio-util = { version = "^0", features = ["io"] }
json-pointer = "^0"
itertools = "^0"
regex = "^1"
deno_fetch = "0.179.0"
deno_tls = "0.142.0"
deno_console = "0.155.0"
deno_url = "0.155.0"
deno_webidl = "0.155.0"
deno_web = "0.186.0"
deno_net = "0.147.0"
deno_core = "0.284.0"
deno_ast = { version = "0.39.0", features = ["transpiling"] }
deno_fetch = "0.182.0"
deno_tls = "0.145.0"
deno_console = "0.158.0"
deno_url = "0.158.0"
deno_webidl = "0.158.0"
deno_web = "0.189.0"
deno_net = "0.150.0"
deno_core = "0.290.0"
deno_ast = { version = "=0.39.2", features = ["transpiling"] }
async-recursion = "^1"
swc_common = "=0.33.26"
swc_ecma_parser = "=0.144.1"
swc_ecma_ast = "=0.113.4"
swc_ecma_parser = "=0.144.3"
swc_ecma_ast = "=0.113.7"
swc_ecma_visit = "=0.99.1"
base64 = "0.21.0"
hmac = "0.12.1"
@@ -213,7 +213,7 @@ wasm-bindgen-test = "0.3.0"
convert_case = "0.6.0"
getrandom = "0.2"
tokio-postgres = {version = "^0.7", features = ["array-impls", "with-serde_json-1", "with-chrono-0_4", "with-uuid-1", "with-bit-vec-0_6"]}
bit-vec = "0.6.3"
bit-vec = "=0.6.3"
mysql_async = { version = "*", default-features = false, features = ["minimal", "default", "native-tls-tls"]}
postgres-native-tls = "^0"
native-tls = "^0"

View File

@@ -1 +1 @@
6aa8832322866e9f6ff90979db9b2b8f4bf6d4ce
e9917f62ebc48bdd93f9ddda5a58ba6ce8a74ed9

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,4 @@
-- Add up migration script here
alter table resource add column edited_at timestamptz, add column created_by varchar(50);
alter table resource_type add column edited_at timestamptz, add column created_by varchar(50);
alter table folder add column summary text, add column edited_at timestamptz, add column created_by varchar(50);

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,2 @@
-- Add up migration script here
alter table schedule add column paused_until timestamptz;

View File

@@ -8,7 +8,7 @@ use std::{
};
use rsmq_async::MultiplexedRsmq;
use serde::{de::DeserializeOwned, Serialize};
use serde::de::DeserializeOwned;
use sqlx::{Pool, Postgres};
use tokio::{
join,
@@ -38,7 +38,6 @@ use windmill_common::{
jobs::QueuedJob,
oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH,
server::load_server_config,
stats_ee::get_user_usage,
users::truncate_token,
utils::{now_from_db, rd_string},
worker::{
@@ -786,17 +785,10 @@ pub async fn monitor_db(
}
};
let save_usage_metrics_f = async {
if !initial_load && server_mode {
save_usage_metrics(&db).await;
}
};
join!(
expired_items_f,
zombie_jobs_f,
expose_queue_metrics_f,
save_usage_metrics_f,
verify_license_key_f
);
}
@@ -871,89 +863,6 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
}
}
#[derive(Serialize)]
struct WorkerUsage {
worker: String,
worker_instance: String,
vcpus: Option<i64>,
memory: Option<i64>,
}
pub async fn save_usage_metrics(db: &Pool<Postgres>) {
let tx = db.begin().await;
if let Ok(mut tx) = tx {
let last_check = sqlx::query_scalar!(
"SELECT created_at FROM metrics WHERE id = 'author_count' ORDER BY created_at DESC LIMIT 1"
)
.fetch_optional(db)
.await
.unwrap_or(Some(chrono::Utc::now()));
let random_nb = rand::random::<i64>();
// save author and operator count every ~24 hours
if last_check
.map(|last_check| {
chrono::Utc::now() - last_check
> chrono::Duration::hours(24) - chrono::Duration::minutes(random_nb % 60)
})
.unwrap_or(true)
{
let user_usage = get_user_usage(&mut *tx).await.ok();
if let Some(user_usage) = user_usage {
sqlx::query!(
"INSERT INTO metrics (id, value) VALUES ('author_count', $1), ('operator_count', $2)",
serde_json::json!(user_usage.author_count.unwrap_or(0)),
serde_json::json!(user_usage.operator_count.unwrap_or(0))
)
.execute(&mut *tx)
.await
.ok();
}
// clean metrics older than 6 months (including worker usage)
sqlx::query!(
"DELETE FROM metrics
WHERE (id = 'author_count' OR id = 'operator_count' OR id = 'worker_usage') AND created_at < NOW() - INTERVAL '6 month'"
)
.execute(&mut *tx)
.await
.ok();
}
// save worker usage every ~60 minutes
if last_check
.map(|last_check| {
chrono::Utc::now() - last_check
> chrono::Duration::minutes(60) - chrono::Duration::seconds(random_nb % 300)
})
.unwrap_or(true)
{
let worker_usage = sqlx::query_as!(
WorkerUsage,
"SELECT worker, worker_instance, vcpus, memory FROM worker_ping WHERE ping_at > NOW() - INTERVAL '2 minutes'"
)
.fetch_all(&mut *tx)
.await
.ok();
if let Some(worker_usage) = worker_usage {
sqlx::query!(
"INSERT INTO metrics (id, value) VALUES ('worker_usage', $1)",
serde_json::json!(worker_usage)
)
.execute(&mut *tx)
.await
.ok();
}
}
tx.commit().await.ok();
}
}
pub async fn reload_server_config(db: &Pool<Postgres>) {
let config = load_server_config(&db).await;
if let Err(e) = config {

View File

@@ -2938,6 +2938,7 @@ async fn test_script_schedule_handlers(db: Pool<Postgres>) {
no_flow_overlap: None,
summary: None,
tag: None,
paused_until: None,
};
let _ = client.create_schedule("test-workspace", &schedule).await;
@@ -3002,6 +3003,7 @@ async fn test_script_schedule_handlers(db: Pool<Postgres>) {
summary: None,
no_flow_overlap: None,
tag: None,
paused_until: None,
},
)
.await
@@ -3081,6 +3083,7 @@ async fn test_flow_schedule_handlers(db: Pool<Postgres>) {
no_flow_overlap: None,
summary: None,
tag: None,
paused_until: None,
};
let _ = client.create_schedule("test-workspace", &schedule).await;
@@ -3146,6 +3149,7 @@ async fn test_flow_schedule_handlers(db: Pool<Postgres>) {
summary: None,
no_flow_overlap: None,
tag: None,
paused_until: None,
},
)
.await

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.350.1
version: 1.355.0
title: Windmill API
contact:
@@ -2373,6 +2373,10 @@ paths:
- variable
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- name: path_start
in: query
schema:
type: string
responses:
"200":
description: variable list
@@ -2854,6 +2858,10 @@ paths:
in: query
schema:
type: string
- name: path_start
in: query
schema:
type: string
responses:
"200":
description: resource list
@@ -3494,17 +3502,24 @@ paths:
- name: show_archived
description: |
(default false)
show also the archived files.
show only the archived files.
when multiple archived hash share the same path, only the ones with the latest create_at
are
ed.
in: query
schema:
type: boolean
- name: hide_without_main
- name: include_without_main
description: |
(default false)
hide the scripts without an exported main function
include scripts without an exported main function
in: query
schema:
type: boolean
- name: include_draft_only
description: |
(default false)
include scripts that have no deployed version
in: query
schema:
type: boolean
@@ -4253,7 +4268,7 @@ paths:
- name: show_archived
description: |
(default false)
show also the archived files.
show only the archived files.
when multiple archived hash share the same path, only the ones with the latest create_at
are displayed.
in: query
@@ -4266,6 +4281,13 @@ paths:
in: query
schema:
type: boolean
- name: include_draft_only
description: |
(default false)
include items that have no deployed version
in: query
schema:
type: boolean
responses:
"200":
description: All flow
@@ -4619,6 +4641,13 @@ paths:
in: query
schema:
type: boolean
- name: include_draft_only
description: |
(default false)
include items that have no deployed version
in: query
schema:
type: boolean
responses:
"200":
description: All apps
@@ -6440,6 +6469,10 @@ paths:
in: query
schema:
type: boolean
- name: path_start
in: query
schema:
type: string
responses:
"200":
description: schedule list
@@ -6917,6 +6950,8 @@ paths:
properties:
name:
type: string
summary:
type: string
owners:
type: array
items:
@@ -6951,6 +6986,8 @@ paths:
schema:
type: object
properties:
summary:
type: string
owners:
type: array
items:
@@ -9842,6 +9879,11 @@ components:
type: object
additionalProperties:
type: boolean
created_by:
type: string
edited_at:
type: string
format: date-time
required:
- path
- resource_type
@@ -9875,6 +9917,11 @@ components:
type: boolean
account:
type: number
created_by:
type: string
edited_at:
type: string
format: date-time
required:
- path
- resource_type
@@ -9892,6 +9939,11 @@ components:
schema: {}
description:
type: string
created_by:
type: string
edited_at:
type: string
format: date-time
required:
- name
@@ -9957,6 +10009,9 @@ components:
type: boolean
tag:
type: string
paused_until:
type: string
format: date-time
required:
- path
- edited_by
@@ -10032,6 +10087,9 @@ components:
type: string
tag:
type: string
paused_until:
type: string
format: date-time
required:
- path
- schedule
@@ -10074,6 +10132,9 @@ components:
type: string
tag:
type: string
paused_until:
type: string
format: date-time
required:
- schedule
- timezone
@@ -10126,6 +10187,13 @@ components:
type: object
additionalProperties:
type: boolean
summary:
type: string
created_by:
type: string
edited_at:
type: string
format: date-time
required:
- name
- owners

View File

@@ -279,6 +279,18 @@ async fn list_apps(
sqlb.and_where_is_not_null("favorite.path");
}
if let Some(path_start) = &lq.path_start {
sqlb.and_where_like_left("app.path", path_start);
}
if let Some(path_exact) = &lq.path_exact {
sqlb.and_where_eq("app.path", "?".bind(path_exact));
}
if !lq.include_draft_only.unwrap_or(false) || authed.is_operator {
sqlb.and_where("app.draft_only IS NOT TRUE");
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, ListableApp>(&sql)

View File

@@ -143,10 +143,10 @@ async fn list_flows(
sqlb.and_where_eq("archived", lq.show_archived.unwrap_or(false));
if let Some(ps) = &lq.path_start {
sqlb.and_where_like_left("path", "?".bind(ps));
sqlb.and_where_like_left("o.path", ps);
}
if let Some(p) = &lq.path_exact {
sqlb.and_where_eq("path", "?".bind(p));
sqlb.and_where_eq("o.path", "?".bind(p));
}
if let Some(cb) = &lq.edited_by {
sqlb.and_where_eq("edited_by", "?".bind(cb));
@@ -155,6 +155,10 @@ async fn list_flows(
sqlb.and_where_is_not_null("favorite.path");
}
if !lq.include_draft_only.unwrap_or(false) || authed.is_operator {
sqlb.and_where("o.draft_only IS NOT TRUE");
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, ListableFlow>(&sql)

View File

@@ -56,11 +56,15 @@ pub struct Folder {
pub display_name: String,
pub owners: Vec<String>,
pub extra_perms: serde_json::Value,
pub summary: Option<String>,
pub created_by: Option<String>,
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(Deserialize)]
pub struct NewFolder {
pub name: String,
pub summary: Option<String>,
pub display_name: Option<String>,
pub owners: Option<Vec<String>>,
pub extra_perms: Option<serde_json::Value>,
@@ -68,6 +72,7 @@ pub struct NewFolder {
#[derive(Deserialize)]
pub struct UpdateFolder {
pub summary: Option<String>,
pub display_name: Option<String>,
pub owners: Option<Vec<String>>,
pub extra_perms: Option<serde_json::Value>,
@@ -90,7 +95,7 @@ async fn list_folders(
let rows = sqlx::query_as!(
Folder,
"SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
"SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
w_id,
per_page as i64,
offset as i64
@@ -199,12 +204,14 @@ async fn create_folder(
sqlx::query_as!(
Folder,
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, $2, $3, $4, $5)",
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, now())",
w_id,
ng.name,
ng.display_name.unwrap_or(ng.name.clone()),
&owners,
extra_perms,
ng.summary,
authed.username
)
.execute(&mut *tx)
.await?;
@@ -283,6 +290,12 @@ async fn update_folder(
sqlb.set("display_name", "?".bind(&display_name));
}
if let Some(summary) = ng.summary {
sqlb.set("summary", "?".bind(&summary));
}
sqlb.set("edited_at", "now()");
if !authed.is_admin {
let prefixed_username = format!("u/{}", authed.username);
if ng.owners.as_ref().is_some_and(|x| {
@@ -380,7 +393,7 @@ pub async fn get_folderopt<'c>(
) -> Result<Option<Folder>> {
let folderopt = sqlx::query_as!(
Folder,
"SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE name = $1 AND workspace_id = $2",
"SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE name = $1 AND workspace_id = $2",
name,
w_id
)

View File

@@ -3767,7 +3767,7 @@ async fn run_flow_dependencies_job(
PushIsolationLevel::IsolatedRoot(db.clone(), rsmq),
&w_id,
JobPayload::RawFlowDependencies { path: req.path, flow_value: req.flow_value },
PushArgs::empty(),
HashMap::from([("skip_flow_update".to_string(), to_raw_value(&true))]).into(),
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),

View File

@@ -102,6 +102,14 @@ async fn list_apps(
sqlb.and_where_is_not_null("favorite.path");
}
if let Some(path_start) = &lq.path_start {
sqlb.and_where_like_left("app.path", path_start);
}
if let Some(path_exact) = &lq.path_exact {
sqlb.and_where_eq("app.path", "?".bind(path_exact));
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, ListableApp>(&sql)

View File

@@ -69,6 +69,8 @@ pub struct ResourceType {
pub name: String,
pub schema: Option<serde_json::Value>,
pub description: Option<String>,
pub created_by: Option<String>,
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(Deserialize)]
@@ -92,6 +94,8 @@ pub struct Resource {
pub description: Option<String>,
pub resource_type: String,
pub extra_perms: serde_json::Value,
pub created_by: Option<String>,
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(FromRow, Serialize, Deserialize)]
@@ -102,6 +106,8 @@ pub struct ListableResource {
pub description: Option<String>,
pub resource_type: String,
pub extra_perms: serde_json::Value,
pub created_by: Option<String>,
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
pub is_linked: Option<bool>,
pub is_refreshed: Option<bool>,
pub is_oauth: Option<bool>,
@@ -128,6 +134,7 @@ struct EditResource {
pub struct ListResourceQuery {
resource_type: Option<String>,
resource_type_exclude: Option<String>,
path_start: Option<String>,
}
#[derive(Serialize, FromRow)]
@@ -209,6 +216,8 @@ async fn list_resources(
"variable.is_oauth",
"variable.account",
"account.refresh_error",
"resource.created_by",
"resource.edited_at",
])
.left()
.join("variable")
@@ -241,6 +250,10 @@ async fn list_resources(
}
}
if let Some(path_start) = &lq.path_start {
sqlb.and_where_like_left("resource.path", path_start);
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, ListableResource>(&sql)
@@ -642,14 +655,15 @@ async fn create_resource(
sqlx::query!(
"INSERT INTO resource
(workspace_id, path, value, description, resource_type)
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path)
DO UPDATE SET value = $3, description = $4, resource_type = $5",
(workspace_id, path, value, description, resource_type, created_by, edited_at)
VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)
DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
w_id,
resource.path,
raw_json as sqlx::types::Json<&RawValue>,
resource.description,
resource.resource_type,
authed.username
)
.execute(&mut *tx)
.await?;
@@ -772,6 +786,8 @@ async fn update_resource(
sqlb.set_str("description", ndesc);
}
sqlb.set_str("edited_at", "now()");
sqlb.returning("path");
let authed = maybe_refresh_folders(path, &w_id, authed, &db).await;
@@ -853,7 +869,7 @@ async fn update_resource_value(
let mut tx = user_db.begin(&authed).await?;
sqlx::query!(
"UPDATE resource SET value = $1 WHERE path = $2 AND workspace_id = $3",
"UPDATE resource SET value = $1, edited_at = now() WHERE path = $2 AND workspace_id = $3",
nv.value,
path,
w_id
@@ -979,12 +995,13 @@ async fn create_resource_type(
sqlx::query!(
"INSERT INTO resource_type
(workspace_id, name, schema, description)
VALUES ($1, $2, $3, $4)",
(workspace_id, name, schema, description, created_by, edited_at)
VALUES ($1, $2, $3, $4, $5, now())",
w_id,
resource_type.name,
resource_type.schema,
resource_type.description,
authed.username
)
.execute(&mut *tx)
.await?;
@@ -1120,6 +1137,7 @@ async fn update_resource_type(
if let Some(ndesc) = ns.description {
sqlb.set_str("description", ndesc);
}
sqlb.set_str("edited_at", "now()");
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;

View File

@@ -72,6 +72,7 @@ pub struct NewSchedule {
pub ws_error_handler_muted: Option<bool>,
pub retry: Option<serde_json::Value>,
pub tag: Option<String>,
pub paused_until: Option<DateTime<Utc>>,
}
#[derive(Serialize, Deserialize)]
@@ -157,9 +158,9 @@ async fn create_schedule(
"INSERT INTO schedule (workspace_id, path, schedule, timezone, edited_by, script_path, \
is_flow, args, enabled, email, on_failure, on_failure_times, on_failure_exact, \
on_failure_extra_args, on_recovery, on_recovery_times, on_recovery_extra_args, \
ws_error_handler_muted, retry, summary, no_flow_overlap, tag \
ws_error_handler_muted, retry, summary, no_flow_overlap, tag, paused_until \
) VALUES ( \
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22 \
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23 \
) RETURNING *")
.bind(&w_id)
.bind(&ns.path)
@@ -183,6 +184,7 @@ async fn create_schedule(
.bind(&ns.summary)
.bind(&ns.no_flow_overlap.unwrap_or(false))
.bind(&ns.tag)
.bind(&ns.paused_until)
.fetch_one(&mut tx)
.await
.map_err(|e| Error::InternalErr(format!("inserting schedule in {w_id}: {e:#}")))?;
@@ -247,8 +249,8 @@ async fn edit_schedule(
"UPDATE schedule SET schedule = $1, timezone = $2, args = $3, on_failure = $4, on_failure_times = $5, \
on_failure_exact = $6, on_failure_extra_args = $7, on_recovery = $8, on_recovery_times = $9, \
on_recovery_extra_args = $10, ws_error_handler_muted = $11, retry = $12, summary = $13, \
no_flow_overlap = $14, tag = $15
WHERE path = $16 AND workspace_id = $17 RETURNING *")
no_flow_overlap = $14, tag = $15, paused_until = $16
WHERE path = $17 AND workspace_id = $18 RETURNING *")
.bind(&es.schedule)
.bind(&es.timezone)
.bind(&es.args)
@@ -264,6 +266,7 @@ async fn edit_schedule(
.bind(&es.summary)
.bind(&es.no_flow_overlap.unwrap_or(false))
.bind(&es.tag)
.bind(&es.paused_until)
.bind(&path)
.bind(&w_id)
.fetch_one(&mut tx)
@@ -313,6 +316,7 @@ pub struct ListScheduleQuery {
pub path: Option<String>,
pub is_flow: Option<bool>,
pub args: Option<String>,
pub path_start: Option<String>,
}
async fn list_schedule(
@@ -339,6 +343,9 @@ async fn list_schedule(
if let Some(args) = &lsq.args {
sqlb.and_where("args @> ?".bind(&args.replace("'", "''")));
}
if let Some(path_start) = &lsq.path_start {
sqlb.and_where_like_left("path", path_start);
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let rows = sqlx::query_as::<_, Schedule>(&sql)
.fetch_all(&mut *tx)
@@ -375,6 +382,7 @@ pub struct ScheduleWJobs {
pub summary: Option<String>,
pub no_flow_overlap: bool,
pub tag: Option<String>,
pub paused_until: Option<DateTime<Utc>>,
}
async fn list_schedule_with_jobs(
@@ -786,6 +794,7 @@ pub struct EditSchedule {
pub retry: Option<serde_json::Value>,
pub no_flow_overlap: Option<bool>,
pub tag: Option<String>,
pub paused_until: Option<DateTime<Utc>>,
}
pub async fn clear_schedule<'c>(

View File

@@ -226,10 +226,14 @@ async fn list_scripts(
.limit(per_page)
.clone();
if authed.is_operator || lq.hide_without_main.unwrap_or(false) {
if !lq.include_without_main.unwrap_or(false) || authed.is_operator {
sqlb.and_where("o.no_main_func IS NOT TRUE");
}
if !lq.include_draft_only.unwrap_or(false) || authed.is_operator {
sqlb.and_where("draft_only IS NOT TRUE");
}
if lq.show_archived.unwrap_or(false) {
sqlb.and_where_eq(
"o.created_at",
@@ -242,10 +246,10 @@ async fn list_scripts(
sqlb.and_where_eq("archived", false);
}
if let Some(ps) = &lq.path_start {
sqlb.and_where_like_left("path", "?".bind(ps));
sqlb.and_where_like_left("o.path", ps);
}
if let Some(p) = &lq.path_exact {
sqlb.and_where_eq("path", "?".bind(p));
sqlb.and_where_eq("o.path", "?".bind(p));
}
if let Some(cb) = &lq.created_by {
sqlb.and_where_eq("created_by", "?".bind(cb));

View File

@@ -47,7 +47,7 @@ fn serve_path(path: &str) -> Response<Body> {
let mut res = Response::builder()
.header(header::CONTENT_TYPE, mime.as_ref())
.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");
if mime.as_ref() == mime::APPLICATION_JAVASCRIPT {
if mime.as_ref() == mime::APPLICATION_JAVASCRIPT || path.ends_with(".wasm") {
res = res.header(header::CACHE_CONTROL, "max-age=31536000");
} else if (mime.type_(), mime.subtype()) == (mime::TEXT, mime::CSS) {
res = res.header(header::CACHE_CONTROL, "max-age=31536000");

View File

@@ -589,8 +589,7 @@ where
parts.extensions.insert(authed.clone());
if authed.scopes.is_some()
&& (path_vec.len() < 3
|| (path_vec[4] != "jobs"
&& path_vec[4] != "jobs_u"))
|| (path_vec[4] != "jobs" && path_vec[4] != "jobs_u"))
{
return Err((
StatusCode::UNAUTHORIZED,
@@ -2834,6 +2833,24 @@ async fn update_username_in_workpsace<'c>(
.unwrap();
// ---- resources----
sqlx::query!(
r#"UPDATE resource SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3"#,
new_username,
old_username,
w_id
)
.execute(&mut **tx)
.await?;
sqlx::query!(
r#"UPDATE resource_type SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3"#,
new_username,
old_username,
w_id
)
.execute(&mut **tx)
.await?;
sqlx::query!(
r#"UPDATE resource SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
new_username,
@@ -3037,6 +3054,15 @@ async fn update_username_in_workpsace<'c>(
// ---- folders ----
sqlx::query!(
"UPDATE folder SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
new_username,
old_username,
w_id
)
.execute(&mut **tx)
.await?;
sqlx::query!(
"UPDATE folder SET owners = ARRAY_REPLACE(owners, 'u/' || $2, 'u/' || $1) WHERE ('u/' || $2) = ANY(owners) AND workspace_id = $3",
new_username,

View File

@@ -81,10 +81,16 @@ async fn list_contextual_variables(
))
}
#[derive(Deserialize)]
struct ListVariableQuery {
path_start: Option<String>,
}
async fn list_variables(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path(w_id): Path<String>,
Query(lq): Query<ListVariableQuery>,
) -> JsonResult<Vec<ListableVariable>> {
let mut tx = user_db.begin(&authed).await?;
@@ -97,10 +103,13 @@ async fn list_variables(
from variable
LEFT JOIN account ON variable.account = account.id AND account.workspace_id = $1
LEFT JOIN resource ON resource.path = variable.path AND resource.workspace_id = $1
WHERE variable.workspace_id = $1 AND variable.path NOT LIKE 'u/' || $2 || '/secret_arg/%' ORDER BY path",
WHERE variable.workspace_id = $1 AND variable.path NOT LIKE 'u/' || $2 || '/secret_arg/%'
AND variable.path LIKE $3 || '%'
ORDER BY path",
)
.bind(&w_id)
.bind(&authed.username)
.bind(&lq.path_start.unwrap_or_default())
.fetch_all(&mut *tx)
.await?;
@@ -523,7 +532,7 @@ async fn update_variable(
}
sqlx::query!(
"UPDATE resource SET path = $1, value = $2 WHERE path = $3 AND workspace_id = $4",
"UPDATE resource SET path = $1, value = $2, edited_at = now() WHERE path = $3 AND workspace_id = $4",
npath,
v,
path,

View File

@@ -1605,29 +1605,33 @@ async fn create_workspace(
.await?;
sqlx::query!(
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
nw.id,
username,
)
.execute(&mut *tx)
.await?;
sqlx::query!(
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
nw.id,
username,
)
.execute(&mut *tx)
.await?;
sqlx::query!(
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
nw.id,
username,
)
.execute(&mut *tx)
.await?;
sqlx::query!(
"INSERT INTO resource (workspace_id, path, value, description, resource_type) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme') ON CONFLICT DO NOTHING",
"INSERT INTO resource (workspace_id, path, value, description, resource_type, created_by, edited_at) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme', $2, now()) ON CONFLICT DO NOTHING",
nw.id,
username,
)
.execute(&mut *tx)
.await?;

View File

@@ -11,4 +11,7 @@ use serde::Deserialize;
#[derive(Deserialize)]
pub struct ListAppQuery {
pub starred_only: Option<bool>,
pub path_exact: Option<String>,
pub path_start: Option<String>,
pub include_draft_only: Option<bool>,
}

View File

@@ -551,6 +551,7 @@ pub struct ListFlowQuery {
pub order_by: Option<String>,
pub order_desc: Option<bool>,
pub starred_only: Option<bool>,
pub include_draft_only: Option<bool>,
}
pub fn add_virtual_items_if_necessary(modules: &mut Vec<FlowModule>) {

View File

@@ -51,6 +51,8 @@ pub struct Schedule {
pub summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub paused_until: Option<DateTime<chrono::Utc>>,
}
impl Schedule {

View File

@@ -14,7 +14,7 @@ use std::{
use crate::{
error::{to_anyhow, Error},
utils::http_get_from_hub,
DB, HUB_BASE_URL,
DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL,
};
use anyhow::Context;
use serde::de::Error as _;
@@ -336,7 +336,8 @@ pub struct ListScriptQuery {
pub is_template: Option<bool>,
pub kinds: Option<String>,
pub starred_only: Option<bool>,
pub hide_without_main: Option<bool>,
pub include_without_main: Option<bool>,
pub include_draft_only: Option<bool>,
}
pub fn to_i64(s: &str) -> crate::error::Result<i64> {
@@ -368,9 +369,11 @@ pub async fn get_hub_script_by_path(
.strip_prefix("hub/")
.ok_or_else(|| Error::BadRequest("Impossible to remove prefix hex".to_string()))?;
let content = http_get_from_hub(
let hub_base_url = HUB_BASE_URL.read().await.clone();
let result = http_get_from_hub(
http_client,
&format!("{}/raw/{}.ts", *HUB_BASE_URL.read().await, path),
&format!("{}/raw/{}.ts", hub_base_url, path),
true,
None,
db,
@@ -378,8 +381,39 @@ pub async fn get_hub_script_by_path(
.await?
.text()
.await
.map_err(to_anyhow)?;
Ok(content)
.map_err(to_anyhow);
match result {
Ok(result) => Ok(result),
Err(e) => {
if hub_base_url != DEFAULT_HUB_BASE_URL
&& path
.split("/")
.next()
.is_some_and(|x| x.parse::<i32>().is_ok_and(|x| x < 10_000_000))
{
tracing::info!(
"Not found on private hub, fallback to default hub for {}",
path
);
let content = http_get_from_hub(
http_client,
&format!("{}/raw/{}.ts", DEFAULT_HUB_BASE_URL, path),
true,
None,
db,
)
.await?
.text()
.await
.map_err(to_anyhow)?;
Ok(content)
} else {
Err(e)?
}
}
}
}
pub async fn get_full_hub_script_by_path(
@@ -392,9 +426,11 @@ pub async fn get_full_hub_script_by_path(
.strip_prefix("hub/")
.ok_or_else(|| Error::BadRequest("Impossible to remove prefix hex".to_string()))?;
let value = http_get_from_hub(
let hub_base_url = HUB_BASE_URL.read().await.clone();
let result = http_get_from_hub(
http_client,
&format!("{}/raw2/{}", *HUB_BASE_URL.read().await, path),
&format!("{}/raw2/{}", hub_base_url, path),
true,
None,
db,
@@ -402,8 +438,39 @@ pub async fn get_full_hub_script_by_path(
.await?
.json::<HubScript>()
.await
.context("Decoding hub response to script")?;
Ok(value)
.context("Decoding hub response to script");
match result {
Ok(result) => Ok(result),
Err(e) => {
if hub_base_url != DEFAULT_HUB_BASE_URL
&& path
.split("/")
.next()
.is_some_and(|x| x.parse::<i32>().is_ok_and(|x| x < 10_000_000))
{
tracing::info!(
"Not found on private hub, fallback to default hub for {}",
path
);
let value = http_get_from_hub(
http_client,
&format!("{}/raw2/{}", DEFAULT_HUB_BASE_URL, path),
true,
None,
db,
)
.await?
.json::<HubScript>()
.await
.context("Decoding hub response to script")?;
Ok(value)
} else {
Err(e)?
}
}
}
}
#[derive(Deserialize, Serialize)]

View File

@@ -9,6 +9,7 @@
use crate::push;
use crate::PushIsolationLevel;
use crate::QueueTransaction;
use anyhow::Context;
use sqlx::{query_scalar, Postgres, Transaction};
use std::collections::HashMap;
use std::str::FromStr;
@@ -36,10 +37,27 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
let tz = chrono_tz::Tz::from_str(&schedule.timezone)
.map_err(|e| error::Error::BadRequest(e.to_string()))?;
let now = now_from_db(&mut tx).await?.with_timezone(&tz);
let now = now_from_db(&mut tx).await?;
let starting_from = match schedule.paused_until {
Some(paused_until) if paused_until > now => paused_until.with_timezone(&tz),
paused_until_o => {
if paused_until_o.is_some() {
sqlx::query!(
"UPDATE schedule SET paused_until = NULL WHERE workspace_id = $1 AND path = $2",
&schedule.workspace_id,
&schedule.path
)
.execute(&mut tx)
.await
.context("Failed to clear paused_until for schedule")?;
}
now.with_timezone(&tz)
}
};
let next = sched
.after(&now)
.after(&starting_from)
.next()
.expect("a schedule should have a next event");

View File

@@ -85,7 +85,10 @@ pub async fn gen_lockfile(
.replace("W_ID", w_id)
.replace("BASE_INTERNAL_URL", base_internal_url)
.replace("TOKEN", token)
.replace("CURRENT_PATH", script_path)
.replace(
"CURRENT_PATH",
&crate::common::use_flow_root_path(script_path)
)
.replace("RAW_GET_ENDPOINT", "raw")
),
)
@@ -305,7 +308,10 @@ pub async fn build_loader(
.replace("W_ID", w_id)
.replace("BASE_INTERNAL_URL", base_internal_url)
.replace("TOKEN", token)
.replace("CURRENT_PATH", current_path)
.replace(
"CURRENT_PATH",
&crate::common::use_flow_root_path(current_path),
)
.replace("RAW_GET_ENDPOINT", "raw_unpinned");
if nodejs_mode {
write_file(

View File

@@ -1568,13 +1568,14 @@ pub async fn save_in_cache(
if let Err(e) = sqlx::query!(
"INSERT INTO resource
(workspace_id, path, value, resource_type)
VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path)
DO UPDATE SET value = $3",
(workspace_id, path, value, resource_type, created_by, edited_at)
VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)
DO UPDATE SET value = $3, edited_at = now()",
job.workspace_id,
cached_path,
raw_json as sqlx::types::Json<CachedResource>,
"cache"
"cache",
job.created_by
)
.execute(db)
.await
@@ -1599,3 +1600,19 @@ pub async fn clean_cache() -> error::Result<()> {
tracing::info!("Finished cleaning cache");
Ok(())
}
lazy_static::lazy_static! {
static ref RE_FLOW_ROOT: Regex = Regex::new(r"(?i)(.*?)(?:/branchone-\d+/|/branchall-\d+/|/loop-\d+/)").unwrap();
}
pub fn use_flow_root_path(flow_path: &str) -> String {
if let Some(captures) = RE_FLOW_ROOT.captures(flow_path) {
return captures
.get(1)
.map(|m| format!("{}/flow", m.as_str()))
.unwrap_or_else(|| flow_path.to_string());
} else {
return flow_path.to_string();
}
}

View File

@@ -355,7 +355,8 @@ async fn build_import_map(
base_internal_url: &str,
job_dir: &str,
) -> error::Result<()> {
let script_path_split = script_path.split("/");
let rooted_path = crate::common::use_flow_root_path(script_path);
let script_path_split = rooted_path.split("/");
let script_path_parts_len = script_path_split.clone().count();
let mut relative_mounts = "".to_string();
for c in 0..script_path_parts_len {

View File

@@ -247,13 +247,13 @@ pub async fn handle_python_job(
base_internal_url: &str,
envs: HashMap<String, String>,
) -> windmill_common::error::Result<Box<RawValue>> {
let script_path = job.script_path();
let script_path = crate::common::use_flow_root_path(job.script_path());
let additional_python_paths = handle_python_deps(
job_dir,
requirements_o,
inner_content,
&job.workspace_id,
script_path,
&script_path,
&job.id,
db,
worker_name,
@@ -281,7 +281,7 @@ pub async fn handle_python_job(
transforms,
spread,
main_name,
) = prepare_wrapper(job_dir, inner_content, script_path, job.args.as_ref()).await?;
) = prepare_wrapper(job_dir, inner_content, &script_path, job.args.as_ref()).await?;
create_args_and_out_file(&client, job, job_dir, db).await?;

View File

@@ -6621,8 +6621,8 @@ var ScriptService = class {
* are
* ed.
*
* @param data.hideWithoutMain (default false)
* hide the scripts without an exported main function
* @param data.includeWithoutMain (default false)
* include scripts without an exported main function
*
* @param data.isTemplate (default regardless)
* if true show only the templates
@@ -6656,7 +6656,7 @@ var ScriptService = class {
last_parent_hash: data.lastParentHash,
parent_hash: data.parentHash,
show_archived: data.showArchived,
hide_without_main: data.hideWithoutMain,
includeWithoutMain: data.includeWithoutMain,
is_template: data.isTemplate,
kinds: data.kinds,
starred_only: data.starredOnly,

View File

@@ -303,10 +303,18 @@ pub async fn update_flow_status_after_job_completion_internal<
let skip_branch_failure = match module_status {
FlowStatusModule::InProgress {
branchall: Some(BranchAllStatus { branch, .. }),
parallel,
..
} => compute_skip_branchall_failure(flow, old_status.step, *branch, db)
.await?
.unwrap_or(false),
} => compute_skip_branchall_failure(
flow,
job_id_for_status,
old_status.step,
*branch,
*parallel,
db,
)
.await?
.unwrap_or(false),
_ => false,
};
@@ -425,29 +433,18 @@ pub async fn update_flow_status_after_job_completion_internal<
tx.commit().await?;
if parallelism.is_some() {
// this ensure that the lock is taken in the same order and thus avoid deadlocks
let ids = sqlx::query_scalar!(
"SELECT id FROM queue WHERE parent_job = $1 AND suspend > 0 ORDER by suspend",
flow
sqlx::query!(
"UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2",
flow,
nindex
)
.fetch_all(db)
.execute(db)
.await
.map_err(|e| {
Error::InternalErr(format!("error while locking jobs to decrease parallelism of: {e:#}"))
Error::InternalErr(format!(
"error resuming job at suspend {nindex} and parent {flow}: {e:#}"
))
})?;
for id in ids {
sqlx::query!(
"UPDATE queue SET suspend = suspend - 1 WHERE id = $1 AND suspend > 0",
id
)
.execute(db)
.await
.map_err(|e| {
Error::InternalErr(format!(
"error decreasing suspend for {id}: {e:#}"
))
})?;
}
}
sqlx::query!(
@@ -975,17 +972,41 @@ async fn compute_skip_loop_failures_and_parallelism(
async fn compute_skip_branchall_failure<'c>(
flow: Uuid,
job: &Uuid,
step: i32,
branch: usize,
parallel: bool,
db: &DB,
) -> Result<Option<bool>, Error> {
let branch = if parallel {
sqlx::query_scalar!("SELECT script_path FROM completed_job WHERE id = $1", job)
.fetch_one(db)
.await
.map_err(|e| {
Error::InternalErr(format!("error during retrieval of branchall index: {e:#}"))
})?
.map(|p| {
BRANCHALL_INDEX_RE
.captures(&p)
.map(|x| x.get(1).unwrap().as_str().parse::<i32>().ok())
.flatten()
.ok_or(Error::InternalErr(format!(
"could not parse branchall index from path: {p}"
)))
})
.ok_or_else(|| {
Error::InternalErr(format!("no branchall script path found for job {job}"))
})??
} else {
branch as i32
};
sqlx::query_as(
"SELECT (raw_flow->'modules'->$1->'value'->'branches'->$2->>'skip_failure')::bool
FROM queue
WHERE id = $3",
)
.bind(step)
.bind(branch as i32)
.bind(branch)
.bind(flow)
.fetch_one(db)
.await
@@ -1292,6 +1313,8 @@ lazy_static::lazy_static! {
.and_then(|x| x.parse::<usize>().ok());
static ref CRASH_STEP_COUNTER: AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
static ref BRANCHALL_INDEX_RE: regex::Regex = regex::Regex::new(r"/branchall-(\d+)$").unwrap();
}
#[inline(always)]
@@ -1986,6 +2009,17 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
let mut tx: QueueTransaction<'_, R> = (rsmq.clone(), db.begin().await?).into();
for i in (0..len).into_iter() {
if i % 100 == 0 && i != 0 {
tracing::info!(id = %flow_job.id, root_id = %job_root, "pushed (non-commited yet) first {i} subflows of {len}");
sqlx::query!(
"UPDATE queue
SET last_ping = now()
WHERE id = $1 AND last_ping < now()",
flow_job.id,
)
.execute(db)
.await?;
}
tracing::debug!(id = %flow_job.id, root_id = %job_root, "pushing job {i} of {len}");
let payload_tag = match &job_payloads {
ContinuePayload::SingleJob(payload) => payload.clone(),

View File

@@ -527,15 +527,27 @@ pub async fn handle_flow_dependency_job<R: rsmq_async::RsmqConnection + Send + S
"status": "Flow lock generation was canceled",
})));
}
let skip_flow_update = job
.args
.as_ref()
.map(|x| {
x.get("skip_flow_update")
.map(|v| serde_json::from_str::<bool>(v.get()).ok())
.flatten()
})
.flatten()
.unwrap_or(false);
sqlx::query!(
"UPDATE flow SET value = $1 WHERE path = $2 AND workspace_id = $3",
new_flow_value,
job_path,
job.workspace_id
)
.execute(db)
.await?;
if !skip_flow_update {
sqlx::query!(
"UPDATE flow SET value = $1 WHERE path = $2 AND workspace_id = $3",
new_flow_value,
job_path,
job.workspace_id
)
.execute(db)
.await?;
}
tx.commit().await?;
if let Err(e) = handle_deployment_metadata(
@@ -785,7 +797,10 @@ async fn lock_modules<'c>(
worker_dir,
base_internal_url,
token,
&path.clone().unwrap_or_else(|| job_path.to_string()),
&format!(
"{}/flow",
&path.clone().unwrap_or_else(|| job_path.to_string())
),
false,
)
.await;
@@ -913,7 +928,7 @@ async fn lock_modules_app(
worker_dir,
base_internal_url,
token,
job.script_path(),
&format!("{}/app", job.script_path()),
false,
)
.await;

View File

@@ -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.350.1";
export const VERSION = "v1.355.0";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({

View File

@@ -12,6 +12,7 @@ import {
yamlParse,
} from "./deps.ts";
import { GlobalOptions, isSuperset } from "./types.ts";
import { readInlinePathSync } from "./utils.ts";
export interface AppFile {
value: any;
@@ -31,13 +32,13 @@ export async function pushApp(
return;
}
alreadySynced.push(localPath);
remotePath.replaceAll(SEP, "/");
let app: any = undefined;
// deleting old app if it exists in raw mode
try {
app = await AppService.getAppByPath({
workspace,
path: remotePath.replaceAll("\\", "/"),
path: remotePath,
});
} catch {
//ignore
@@ -59,11 +60,11 @@ export async function pushApp(
const o: Record<string, any> = v as any;
if (o["content"] && o["content"].startsWith("!inline")) {
const basePath = localPath + o["content"].split(" ")[1];
o["content"] = Deno.readTextFileSync(basePath);
o["content"] = readInlinePathSync(basePath);
}
if (o["lock"] && o["lock"].startsWith("!inline")) {
const basePath = localPath + o["lock"].split(" ")[1];
o["lock"] = Deno.readTextFileSync(basePath);
o["lock"] = readInlinePathSync(basePath);
}
} else {
replaceInlineScripts(v);
@@ -83,7 +84,7 @@ export async function pushApp(
log.info(colors.bold.yellow(`Updating app ${remotePath}...`));
await AppService.updateApp({
workspace,
path: remotePath.replaceAll("\\", "/"),
path: remotePath,
requestBody: {
deployment_message: message,
...localApp,
@@ -95,7 +96,7 @@ export async function pushApp(
await AppService.createApp({
workspace,
requestBody: {
path: remotePath.replaceAll("\\", "/"),
path: remotePath,
deployment_message: message,
...localApp,
},
@@ -103,7 +104,7 @@ export async function pushApp(
}
}
async function list(opts: GlobalOptions) {
async function list(opts: GlobalOptions & { includeDraftOnly?: boolean }) {
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
@@ -115,6 +116,7 @@ async function list(opts: GlobalOptions) {
workspace: workspace.workspaceId,
page,
perPage,
includeDraftOnly: opts.includeDraftOnly ?? false,
});
page += 1;
total.push(...res);

View File

@@ -1,24 +1,33 @@
export type EnumType = string[] | undefined;
export interface SchemaProperty {
type: string | undefined
description?: string
pattern?: string
default?: any
enum?: string[]
contentEncoding?: 'base64' | 'binary'
format?: string
items?: {
type?: 'string' | 'number' | 'bytes' | 'object'
contentEncoding?: 'base64'
enum?: string[]
}
min?: number
max?: number
currency?: string
currencyLocale?: string
multiselect?: boolean
customErrorMessage?: string
properties?: { [name: string]: SchemaProperty }
required?: string[]
showExpr?: string
type: string | undefined;
description?: string;
pattern?: string;
default?: any;
enum?: EnumType;
contentEncoding?: "base64" | "binary";
format?: string;
items?: {
type?: "string" | "number" | "bytes" | "object";
contentEncoding?: "base64";
enum?: string[];
};
min?: number;
max?: number;
currency?: string;
currencyLocale?: string;
multiselect?: boolean;
customErrorMessage?: string;
properties?: { [name: string]: SchemaProperty };
required?: string[];
showExpr?: string;
password?: boolean;
order?: string[];
nullable?: boolean;
dateFormat?: string;
title?: string;
placeholder?: string;
oneOf?: SchemaProperty[];
originalType?: string;
}

View File

@@ -12,11 +12,12 @@ import {
yamlParse,
} from "./deps.ts";
import { requireLogin, resolveWorkspace, validatePath } from "./context.ts";
import { exts, resolve, track_job } from "./script.ts";
import { resolve, track_job } from "./script.ts";
import { defaultFlowDefinition } from "./bootstrap/flow_bootstrap.ts";
import { generateFlowLockInternal } from "./metadata.ts";
import { SyncOptions, mergeConfigWithConfigFile } from "./conf.ts";
import { FSFSElement, elementsToMap, ignoreF } from "./sync.ts";
import { readInlinePathSync } from "./utils.ts";
export interface FlowFile {
summary: string;
@@ -46,7 +47,7 @@ export function replaceInlineScripts(
) {
const path = lock.split(" ")[1];
try {
m.value.lock = Deno.readTextFileSync(localPath + path);
m.value.lock = readInlinePathSync(localPath + path);
} catch {
log.error(`Lock file ${path} not found`);
}
@@ -78,6 +79,7 @@ export async function pushFlow(
return;
}
alreadySynced.push(localPath);
remotePath = remotePath.replaceAll(SEP, "/");
let flow: Flow | undefined = undefined;
try {
flow = await FlowService.getFlowByPath({
@@ -104,9 +106,9 @@ export async function pushFlow(
log.info(colors.bold.yellow(`Updating flow ${remotePath}...`));
await FlowService.updateFlow({
workspace: workspace,
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
requestBody: {
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
deployment_message: message,
...localFlow,
},
@@ -116,7 +118,7 @@ export async function pushFlow(
await FlowService.createFlow({
workspace: workspace,
requestBody: {
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
deployment_message: message,
...localFlow,
},
@@ -137,7 +139,9 @@ async function push(opts: Options, filePath: string, remotePath: string) {
log.info(colors.bold.underline.green("Flow pushed"));
}
async function list(opts: GlobalOptions & { showArchived?: boolean }) {
async function list(
opts: GlobalOptions & { showArchived?: boolean; includeDraftOnly?: boolean }
) {
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
@@ -150,6 +154,7 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
page,
perPage,
showArchived: opts.showArchived ?? false,
includeDraftOnly: opts.includeDraftOnly ?? false,
});
page += 1;
total.push(...res);

View File

@@ -33,7 +33,7 @@ addEventListener("error", (event) => {
}
});
export const VERSION = "v1.350.1";
export const VERSION = "v1.355.0";
let command: any = new Command()
.name("wmill")

View File

@@ -39,7 +39,7 @@ import {
newPathAssigner,
yamlOptions,
} from "./sync.ts";
import { generateHash } from "./utils.ts";
import { generateHash, readInlinePathSync } from "./utils.ts";
import { SyncCodebase } from "./codebase.ts";
import { FlowFile, replaceInlineScripts } from "./flow.ts";
@@ -100,11 +100,11 @@ export async function generateFlowLockInternal(
workspace: Workspace,
justUpdateMetadataLock?: boolean
): Promise<string | undefined> {
if (folder.endsWith("/")) {
if (folder.endsWith(SEP)) {
folder = folder.substring(0, folder.length - 1);
}
const remote_path = folder
.replaceAll("\\", "/")
.replaceAll(SEP, "/")
.substring(0, folder.length - ".flow".length);
if (!justUpdateMetadataLock) {
log.info(`Generating lock for flow ${folder} at ${remote_path}`);
@@ -185,7 +185,7 @@ export async function generateScriptMetadataInternal(
): Promise<string | undefined> {
const remotePath = scriptPath
.substring(0, scriptPath.indexOf("."))
.replaceAll("\\", "/");
.replaceAll(SEP, "/");
const language = inferContentTypeFromFilePath(scriptPath, opts.defaultTs);
@@ -256,7 +256,8 @@ export async function generateScriptMetadataInternal(
metadataParsedContent.lock = "";
}
} else {
metadataParsedContent.lock = "!inline " + remotePath + ".script.lock";
metadataParsedContent.lock =
"!inline " + remotePath.replaceAll(SEP, "/") + ".script.lock";
}
let metaPath = remotePath + ".script.yaml";
@@ -347,7 +348,7 @@ async function updateScriptLock(
}
const lockPath = remotePath + ".script.lock";
await Deno.writeTextFile(lockPath, lock);
metadataContent.lock = "!inline " + lockPath;
metadataContent.lock = "!inline " + lockPath.replaceAll(SEP, "/");
} catch (e) {
throw new Error(
`Failed to generate lockfile. Status was: ${rawResponse.statusText}, ${responseText}, ${e}`
@@ -513,10 +514,18 @@ export function argSigToJsonSchemaType(
| null;
}
| { str: string[] | null }
| { object: { key: string; typ: any }[] },
| { object: { key: string; typ: any }[] }
| {
oneof: [
{
label: string;
properties: { key: string; typ: any }[];
}
];
},
oldS: SchemaProperty
): void {
const newS: SchemaProperty = { type: "" };
let newS: SchemaProperty = { type: "" };
if (t === "int") {
newS.type = "integer";
} else if (t === "float") {
@@ -535,15 +544,43 @@ export function argSigToJsonSchemaType(
} else if (t === "bytes") {
newS.type = "string";
newS.contentEncoding = "base64";
newS.originalType = "bytes";
} else if (t === "datetime") {
newS.type = "string";
newS.format = "date-time";
} else if (typeof t !== "string" && "oneof" in t) {
newS.type = "object";
if (t.oneof) {
newS.oneOf = t.oneof.map((obj) => {
const oldObjS =
oldS.oneOf?.find((o) => o?.title === obj.label) ?? undefined;
const properties: Record<string, any> = {};
for (const prop of obj.properties) {
if (oldObjS?.properties && prop.key in oldObjS?.properties) {
properties[prop.key] = oldObjS?.properties[prop.key];
} else {
properties[prop.key] = { description: "", type: "" };
}
argSigToJsonSchemaType(prop.typ, properties[prop.key]);
}
return {
type: "object",
title: obj.label,
properties,
order: oldObjS?.order ?? undefined,
};
});
}
} else if (typeof t !== "string" && `object` in t) {
newS.type = "object";
if (t.object) {
const properties: Record<string, any> = {};
for (const prop of t.object) {
properties[prop.key] = {};
if (oldS.properties && prop.key in oldS.properties) {
properties[prop.key] = oldS.properties[prop.key];
} else {
properties[prop.key] = { description: "", type: "" };
}
argSigToJsonSchemaType(prop.typ, properties[prop.key]);
}
newS.properties = properties;
@@ -551,7 +588,11 @@ export function argSigToJsonSchemaType(
} else if (typeof t !== "string" && `str` in t) {
newS.type = "string";
if (t.str) {
newS.originalType = "enum";
newS.enum = t.str;
} else {
newS.originalType = "string";
newS.enum = undefined;
}
} else if (typeof t !== "string" && `resource` in t) {
newS.type = "object";
@@ -573,6 +614,32 @@ export function argSigToJsonSchemaType(
newS.type = "object";
}
const preservedFields = [
"description",
"pattern",
"min",
"max",
"currency",
"currencyLocale",
"multiselect",
"customErrorMessage",
"required",
"showExpr",
"password",
"order",
"dateFormat",
"title",
"placeholder",
];
preservedFields.forEach((field) => {
// @ts-ignore
if (oldS[field] !== undefined) {
// @ts-ignore
newS[field] = oldS[field];
}
});
if (oldS.type != newS.type) {
for (const prop of Object.getOwnPropertyNames(newS)) {
if (prop != "description") {
@@ -602,18 +669,19 @@ export function argSigToJsonSchemaType(
oldS.format = undefined;
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// end of refactoring TODO //
////////////////////////////////////////////////////////////////////////////////////////////
export async function replaceLock(o?: { lock?: string | string[] }) {
export function replaceLock(o?: { lock?: string | string[] }) {
if (Array.isArray(o?.lock)) {
o.lock = o.lock.join("\n");
}
if (o?.lock?.startsWith("!inline ")) {
try {
const lockPath = o?.lock?.split(" ")[1];
o.lock = await Deno.readTextFile(lockPath);
o.lock = readInlinePathSync(lockPath);
} catch (e) {
log.info(
colors.yellow(`Failed to read lockfile, doing as if it was empty: ${e}`)
@@ -647,7 +715,7 @@ export async function parseMetadataFile(
metadataFilePath = scriptPath + ".script.yaml";
await Deno.stat(metadataFilePath);
const payload: any = yamlParse(await Deno.readTextFile(metadataFilePath));
await replaceLock(payload);
replaceLock(payload);
return {
path: metadataFilePath,
@@ -687,7 +755,7 @@ export async function parseMetadataFile(
scriptInitialMetadata = yamlParse(
await Deno.readTextFile(metadataFilePath)
) as ScriptMetadata;
await replaceLock(scriptInitialMetadata);
replaceLock(scriptInitialMetadata);
} catch (e) {
log.info(
colors.yellow(

View File

@@ -12,6 +12,7 @@ import {
log,
Resource,
ResourceService,
SEP,
Table,
} from "./deps.ts";
@@ -32,7 +33,7 @@ export async function pushResource(
try {
resource = await ResourceService.getResource({
workspace: workspace,
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
});
} catch {
// flow doesn't exist
@@ -45,7 +46,7 @@ export async function pushResource(
await ResourceService.updateResource({
workspace: workspace,
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
requestBody: { ...localResource },
});
} else {
@@ -61,7 +62,7 @@ export async function pushResource(
await ResourceService.createResource({
workspace: workspace,
requestBody: {
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
...localResource,
},
});

View File

@@ -143,7 +143,7 @@ export async function handleFile(
alreadySynced.push(path);
const remotePath = path
.substring(0, path.indexOf("."))
.replaceAll("\\", "/");
.replaceAll(SEP, "/");
const language = inferContentTypeFromFilePath(path, opts?.defaultTs);
@@ -186,7 +186,7 @@ export async function handleFile(
try {
remote = await ScriptService.getScriptByPath({
workspace: workspaceId,
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
});
log.debug(`Script ${remotePath} exists on remote`);
} catch {
@@ -214,7 +214,7 @@ export async function handleFile(
content,
description: typed?.description ?? "",
language: language as NewScript["language"],
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
summary: typed?.summary ?? "",
kind: typed?.kind,
lock: typed?.lock,
@@ -440,7 +440,13 @@ export function removeExtensionToPath(path: string): string {
throw new Error("Invalid extension: " + path);
}
async function list(opts: GlobalOptions & { showArchived?: boolean }) {
async function list(
opts: GlobalOptions & {
showArchived?: boolean;
includeWithoutMain?: boolean;
includeDraftOnly?: boolean;
}
) {
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
@@ -453,6 +459,8 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
page,
perPage,
showArchived: opts.showArchived ?? false,
includeWithoutMain: opts.includeWithoutMain ?? false,
includeDraftOnly: opts.includeDraftOnly ?? true,
});
page += 1;
total.push(...res);

View File

@@ -214,11 +214,11 @@ export function extractInlineScriptsForFlows(
const path = basePath + ext;
const content = m.value.content;
const r = [{ path: path, content: content }];
m.value.content = "!inline " + path;
m.value.content = "!inline " + path.replaceAll(SEP, "/");
const lock = m.value.lock;
if (lock && lock != "") {
const lockPath = basePath + "lock";
m.value.lock = "!inline " + lockPath;
m.value.lock = "!inline " + lockPath.replaceAll(SEP, "/");
r.push({ path: lockPath, content: lock });
}
return r;
@@ -264,7 +264,7 @@ export function extractInlineScriptsForApps(
const r = [];
if (o["content"]) {
const content = o["content"];
o["content"] = "!inline " + basePath + ext;
o["content"] = "!inline " + basePath.replaceAll(SEP, "/") + ext;
r.push({
path: basePath + ext,
content: content,
@@ -272,7 +272,7 @@ export function extractInlineScriptsForApps(
}
if (o["lock"] && o["lock"] != "") {
const lock = o["lock"];
o["lock"] = "!inline " + basePath + "lock";
o["lock"] = "!inline " + basePath.replaceAll(SEP, "/") + "lock";
r.push({
path: basePath + "lock",
content: lock,
@@ -437,7 +437,10 @@ function ZipFSElement(
parsed["lock"] != "" &&
parsed["codebase"] == undefined
) {
parsed["lock"] = "!inline " + removeSuffix(p, ".json") + ".lock";
parsed["lock"] =
"!inline " +
removeSuffix(p.replaceAll(SEP, "/"), ".json") +
".lock";
} else {
parsed["lock"] = undefined;
}
@@ -1127,7 +1130,7 @@ async function push(opts: GlobalOptions & SyncOptions) {
log.info(
colors.gray(
"Computing the files to update on the remote to match local (taking .wmillignore into account)"
"Computing the files to update on the remote to match local (taking wmill.yaml includes/excludes into account)"
)
);
const remote = ZipFSElement(

View File

@@ -1,6 +1,6 @@
// deno-lint-ignore-file no-explicit-any
import { colors, log, path, yamlParse, yamlStringify } from "./deps.ts";
import { SEP, colors, log, path, yamlParse, yamlStringify } from "./deps.ts";
import { pushApp } from "./apps.ts";
import { pushFolder } from "./folder.ts";
import { pushFlow } from "./flow.ts";
@@ -111,14 +111,14 @@ export async function pushObj(
const typeEnding = getTypeStrFromPath(p);
if (typeEnding === "app") {
const appName = p.split(".app" + path.sep)[0];
const appName = p.split(".app" + SEP)[0];
await pushApp(workspace, appName, appName + ".app", message);
} else if (typeEnding === "folder") {
await pushFolder(workspace, p, befObj, newObj);
} else if (typeEnding === "variable") {
await pushVariable(workspace, p, befObj, newObj, plainSecrets);
} else if (typeEnding === "flow") {
const flowName = p.split(".flow" + path.sep)[0];
const flowName = p.split(".flow" + SEP)[0];
await pushFlow(workspace, flowName, flowName + ".flow", message);
} else if (typeEnding === "resource") {
await pushResource(workspace, p, befObj, newObj);
@@ -169,10 +169,10 @@ export function getTypeStrFromPath(
| "user"
| "group"
| "settings" {
if (p.includes(".flow" + path.sep)) {
if (p.includes(".flow" + SEP)) {
return "flow";
}
if (p.includes(".app" + path.sep)) {
if (p.includes(".app" + SEP)) {
return "app";
}
const parsed = path.parse(p);

View File

@@ -2,7 +2,7 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck This file is copied from a JS project, so it's not type-safe.
import { log, encodeHex } from "./deps.ts";
import { log, encodeHex, SEP } from "./deps.ts";
export function deepEqual<T>(a: T, b: T): boolean {
if (a === b) return true;
@@ -117,3 +117,11 @@ export async function generateHashFromBuffer(
const hashBuffer = await crypto.subtle.digest("SHA-256", content);
return encodeHex(hashBuffer);
}
// export async function readInlinePath(path: string): Promise<string> {
// return await Deno.readTextFile(path.replaceAll("/", SEP));
// }
export function readInlinePathSync(path: string): string {
return Deno.readTextFileSync(path.replaceAll("/", SEP));
}

View File

@@ -12,6 +12,7 @@ import {
Confirm,
ListableVariable,
log,
SEP,
Table,
VariableService,
} from "./deps.ts";
@@ -60,7 +61,7 @@ export async function pushVariable(
try {
variable = await VariableService.getVariable({
workspace: workspace,
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
decryptSecret: plainSecrets,
includeEncrypted: true,
});
@@ -79,7 +80,7 @@ export async function pushVariable(
await VariableService.updateVariable({
workspace,
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
alreadyEncrypted: !plainSecrets,
requestBody: {
...localVariable,
@@ -93,7 +94,7 @@ export async function pushVariable(
workspace,
alreadyEncrypted: !plainSecrets,
requestBody: {
path: remotePath.replaceAll("\\", "/"),
path: remotePath.replaceAll(SEP, "/"),
...localVariable,
},
});

5
docker/DockerfileCli Normal file
View File

@@ -0,0 +1,5 @@
FROM denoland/deno:1.44.4
RUN deno install -q -A https://raw.githubusercontent.com/windmill-labs/windmill/main/cli/main.ts --name wmill
ENTRYPOINT [ "wmill" ]

3
frontend/.gitignore vendored
View File

@@ -8,4 +8,5 @@ CaddyfileRemoteRuben
tests-out/
storageState.json
.env.production
dist/
dist/
static/tsdocs/

View File

@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.350.1",
"version": "1.355.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.350.1",
"version": "1.355.0",
"license": "AGPL-3.0",
"dependencies": {
"@aws-crypto/sha256-js": "^4.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.350.1",
"version": "1.355.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -244,11 +244,26 @@
"svelte": "./package/components/SchemaForm.svelte",
"default": "./package/components/SchemaForm.svelte"
},
"./components/EditableSchemaWrapper.svelte": {
"types": "./package/components/schema/EditableSchemaWrapper.svelte.d.ts",
"svelte": "./package/components/schema/EditableSchemaWrapper.svelte",
"default": "./package/components/schema/EditableSchemaWrapper.svelte"
},
"./components/ResourceEditor.svelte": {
"types": "./package/components/ResourceEditor.svelte.d.ts",
"svelte": "./package/components/ResourceEditor.svelte",
"default": "./package/components/ResourceEditor.svelte"
},
"./components/SchemaEditor.svelte": {
"types": "./package/components/EditableSchemaForm.svelte.d.ts",
"svelte": "./package/components/EditableSchemaForm.svelte",
"default": "./package/components/EditableSchemaForm.svelte"
},
"./components/ScriptEditor.svelte": {
"types": "./package/components/ScriptEditor.svelte.d.ts",
"svelte": "./package/components/ScriptEditor.svelte",
"default": "./package/components/ScriptEditor.svelte"
},
"./components/scriptEditor/LogPanel.svelte": {
"types": "./package/components/scriptEditor/LogPanel.svelte.d.ts",
"svelte": "./package/components/scriptEditor/LogPanel.svelte",
@@ -328,6 +343,9 @@
"components/scriptEditor/LogPanel.svelte": [
"./package/components/scriptEditor/LogPanel.svelte.d.ts"
],
"components/ScriptEditor.svelte": [
"./package/components/ScriptEditor.svelte.d.ts"
],
"components/common/kbd/Kbd.svelte": [
"./package/components/common/kbd/Kbd.svelte.d.ts"
],
@@ -376,6 +394,9 @@
"components/SchemaEditor.svelte": [
"./package/components/SchemaEditor.svelte.d.ts"
],
"components/EditableSchemaWrapper.svelte": [
"./package/components/schema/EditableSchemaWrapper.svelte.d.ts"
],
"utils": [
"./package/utils.d.ts"
],

View File

@@ -28,33 +28,7 @@
list-style-type: '- ';
padding-left: 3rem;
}
.splitpanes--vertical > .splitpanes__pane {
transition: none !important;
}
.splitpanes--horizontal > .splitpanes__pane {
transition: none !important;
}
.monaco-workbench > .notifications-toasts.visible {
display: none !important;
}
.monaco-editor .suggest-widget {
z-index: 10001 !important;
}
.nowrap pre code.hljs {
whitespace: normal !important;
}
.z5000 {
z-index: 5000 !important;
}
.foo {
color: red !important;
}
.autocomplete-list-item-create {
@apply !text-primary-inverse !bg-surface-inverse;
}
@@ -74,46 +48,8 @@
.autocomplete-list {
@apply !bg-surface;
}
pre code.hljs {
overflow: visible !important;
}
svelte-virtual-list-row {
overflow: visible !important;
}
svelte-virtual-list-contents > * + * {
border-top-width: 1px !important;
border-bottom-width: 0px !important;
}
.monaco-editor textarea:focus {
box-shadow: none !important;
}
.Template-editor span.mtk20 {
color: black !important;
}
::-webkit-scrollbar {
width: 9px;
height: 9px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: rgba(155, 155, 155, 0.5);
border: transparent;
}
}
@layer components {
/* Flow graph viewer -> Svelvet library internal class overwrite */
.Node {
display: flex !important;
}
}
@layer utilities {
@variants responsive {
/* Hide scrollbar for Chrome, Safari and Opera */
@@ -129,33 +65,42 @@
}
}
.grid-cell-centered {
display: flex;
align-items: center;
justify-content: center;
}
.grid-cell-centered .svelte-select {
height: 32px !important;
.splitpanes--vertical > .splitpanes__pane {
transition: none !important;
}
.grid-cell-centered .selected-item {
margin-top: -4px;
.splitpanes--horizontal > .splitpanes__pane {
transition: none !important;
}
.ol-overlaycontainer-stopevent {
@apply flex flex-col justify-start items-end;
}
.ol-control button {
@apply w-7 h-7 center-center bg-surface border text-secondary
rounded mt-1 mr-1 shadow duration-200 hover:bg-surface-hover focus:bg-surface-hover;
.monaco-workbench > .notifications-toasts.visible {
display: none !important;
}
.monaco-editor .suggest-widget {
z-index: 10001 !important;
}
svelte-virtual-list-row {
overflow: visible !important;
}
svelte-virtual-list-contents > * + * {
border-top-width: 1px !important;
border-bottom-width: 0px !important;
}
.monaco-editor textarea:focus {
box-shadow: none !important;
}
/* Components */
.component-wrapper {
@apply rounded-component border overflow-hidden border-gray-300 dark:border-gray-600;
}
.app-editor-input {
@apply rounded-component border border-gray-300 dark:border-gray-500 focus:border-gray-300 focus:dark:border-gray-500 focus:!ring-1 focus:!ring-blue-300;
@apply placeholder:text-gray-400 dark:placeholder:text-gray-600;
@apply placeholder:text-gray-400 dark:placeholder:text-gray-200;
@apply bg-surface;
@apply disabled:placeholder:text-gray-200 disabled:placeholder:dark:text-gray-500 disabled:text-gray-200 disabled:dark:text-gray-500 disabled:border-gray-200 disabled:dark:border-gray-600;
}

View File

@@ -1,68 +0,0 @@
<script lang="ts">
import { onMount } from 'svelte'
import DarkModeObserver from './DarkModeObserver.svelte'
import github from 'svelte-highlight/styles/github'
import nord from 'svelte-highlight/styles/nord'
import { each } from 'chart.js/helpers'
import { Chart } from 'chart.js'
let darkMode: boolean = false
function onThemeChange() {
if (document.documentElement.classList.contains('dark')) {
darkMode = true
each(Chart.instances, (instance) => {
if (instance.options.scales?.y?.ticks?.color !== undefined) {
instance.options.scales.y.ticks.color = '#e0e7ed'
}
if (instance.options.scales?.y?.grid?.color !== undefined) {
instance.options.scales.y.grid.color = '#4a5568'
}
if (instance.options.scales?.x?.ticks?.color !== undefined) {
instance.options.scales.x.ticks.color = '#e0e7ed'
}
if (instance.options.scales?.x?.grid?.color !== undefined) {
instance.options.scales.x.grid.color = '#4a5568'
}
if (instance.options.plugins?.legend?.labels?.color !== undefined) {
instance.options.plugins.legend.labels.color = '#e0e7ed'
}
instance.update()
})
} else {
darkMode = false
each(Chart.instances, (instance) => {
if (instance.options.scales?.y?.ticks?.color !== undefined) {
instance.options.scales.y.ticks.color = '#4a5568'
}
if (instance.options.scales?.y?.grid?.color !== undefined) {
instance.options.scales.y.grid.color = '#e0e7ed'
}
if (instance.options.scales?.x?.ticks?.color !== undefined) {
instance.options.scales.x.ticks.color = '#4a5568'
}
if (instance.options.scales?.x?.grid?.color !== undefined) {
instance.options.scales.x.grid.color = '#e0e7ed'
}
if (instance.options.plugins?.legend?.labels?.color !== undefined) {
instance.options.plugins.legend.labels.color = '#4a5568'
}
instance.update()
})
}
}
onMount(() => {
onThemeChange()
})
</script>
<DarkModeObserver on:change={onThemeChange} />
<svelte:head>
{#if darkMode}
{@html nord}
{:else}
{@html github}
{/if}
</svelte:head>

View File

@@ -2,6 +2,7 @@
import { createEventDispatcher } from 'svelte'
import { Button } from './common'
import { Clock } from 'lucide-svelte'
import { twMerge } from 'tailwind-merge'
// import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
// import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
@@ -11,6 +12,8 @@
export let useDropdown: boolean = false
export let minDate: string | undefined = undefined
export let maxDate: string | undefined = undefined
export let disabled: boolean | undefined = undefined
export let inputClass: string | undefined = undefined
let date: string | undefined = undefined
let time: string | undefined = undefined
@@ -72,17 +75,25 @@
type="date"
bind:value={date}
{autofocus}
class="!w-3/4 app-editor-input"
{disabled}
class={twMerge('h-8 text-sm !w-3/4 ', inputClass)}
min={minDate}
max={maxDate}
/>
<input type="time" bind:value={time} class="!w-1/4 min-w-[100px] app-editor-input" />
<input
type="time"
bind:value={time}
class={twMerge('h-8 text-sm !w-1/4 min-w-[100px] ', inputClass)}
{disabled}
/>
<Button
variant="border"
color="light"
wrapperClasses="h-8"
startIcon={{
icon: Clock
}}
{disabled}
size="xs"
portalTarget={`#${randomId}`}
dropdownItems={useDropdown

Some files were not shown because too many files have changed in this diff Show More