Compare commits
139 Commits
v1.64.0
...
rf/flowFix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ea08f1e8f | ||
|
|
d302c92766 | ||
|
|
ea2ebfa92e | ||
|
|
128622117d | ||
|
|
ba856be10d | ||
|
|
333b873ee9 | ||
|
|
2785b05064 | ||
|
|
a67f10eeb6 | ||
|
|
287b2db22f | ||
|
|
a4e4d188ad | ||
|
|
150ce7f731 | ||
|
|
2244e83b9d | ||
|
|
fae406ee8f | ||
|
|
42d1cd6456 | ||
|
|
4b64e75bd1 | ||
|
|
51a7eaaeb0 | ||
|
|
8589b70ccf | ||
|
|
0bf6f23c9e | ||
|
|
e56869092a | ||
|
|
6b8758f4a5 | ||
|
|
fbc929ba1b | ||
|
|
97602ac6db | ||
|
|
8ee9d67f4f | ||
|
|
4bf6e753f1 | ||
|
|
70eab303bd | ||
|
|
c051ffeb42 | ||
|
|
ebb68e5320 | ||
|
|
04a076f1db | ||
|
|
ebd2e0323e | ||
|
|
cd25570003 | ||
|
|
e95f8ef6bf | ||
|
|
c3d1c8ac39 | ||
|
|
d38aff2fe2 | ||
|
|
95851ea486 | ||
|
|
b690d801d4 | ||
|
|
104e4ac5e7 | ||
|
|
e87f4fc44b | ||
|
|
e1f686d850 | ||
|
|
7da7dac3ac | ||
|
|
c2e5afd4e0 | ||
|
|
ad9c386f41 | ||
|
|
a4e3f98b7d | ||
|
|
dd28308c3c | ||
|
|
833c2655ea | ||
|
|
a8295d0b5a | ||
|
|
897e2f6b53 | ||
|
|
5bb77edf45 | ||
|
|
8ddcf4d9c1 | ||
|
|
33ebe2da8e | ||
|
|
b3ee747014 | ||
|
|
fa105b4cae | ||
|
|
483407cdf0 | ||
|
|
008c30fcaa | ||
|
|
3387bb0d83 | ||
|
|
e08e7e4ae6 | ||
|
|
ea1b2c29b9 | ||
|
|
4ad6fbefd3 | ||
|
|
397ecd64d4 | ||
|
|
dd7e8c742c | ||
|
|
2f78132e08 | ||
|
|
0041411c06 | ||
|
|
54955b710c | ||
|
|
1eb5a0d1d3 | ||
|
|
ddda14c52b | ||
|
|
5123c9365c | ||
|
|
834e7b1d1c | ||
|
|
6e9a5b026e | ||
|
|
7ad8879b09 | ||
|
|
27cac3ffe6 | ||
|
|
705703a5e2 | ||
|
|
fac31c6628 | ||
|
|
90c0e140a1 | ||
|
|
d543650b31 | ||
|
|
089a6b6ae5 | ||
|
|
857ee5f318 | ||
|
|
6ad876ebb4 | ||
|
|
ab4137640e | ||
|
|
2bd8fabcf7 | ||
|
|
3b7160e84a | ||
|
|
40c12e6139 | ||
|
|
6044e3b6ef | ||
|
|
18ff5c7cef | ||
|
|
e54dc3ff97 | ||
|
|
4d5aae69c8 | ||
|
|
ec57c5977f | ||
|
|
df1b724626 | ||
|
|
268dfbf831 | ||
|
|
969e89f8bb | ||
|
|
5997503961 | ||
|
|
3fa24adad0 | ||
|
|
7471be1d81 | ||
|
|
d64e1c116a | ||
|
|
9267b1fb90 | ||
|
|
6528a68668 | ||
|
|
4fd4d17a0d | ||
|
|
0548803ab7 | ||
|
|
0085b46c1e | ||
|
|
81ffd49bef | ||
|
|
dbc59e9521 | ||
|
|
121b3e9060 | ||
|
|
70dfc8b8d0 | ||
|
|
ca3572a2a1 | ||
|
|
32c3c591d7 | ||
|
|
1f4bc55e5c | ||
|
|
a00ff45ccf | ||
|
|
0160ce978d | ||
|
|
867c00047a | ||
|
|
e31d2ae27f | ||
|
|
441f087d42 | ||
|
|
4671558e6b | ||
|
|
08519f4099 | ||
|
|
2727699d91 | ||
|
|
0c43b68b23 | ||
|
|
c280f6e798 | ||
|
|
e81f7bd723 | ||
|
|
2213500210 | ||
|
|
7558fb83d2 | ||
|
|
3d7a5a4520 | ||
|
|
be6f052ba4 | ||
|
|
4f1bcbb1c3 | ||
|
|
a4b773af29 | ||
|
|
61e6e1a4c5 | ||
|
|
cf7dc3c01a | ||
|
|
41c8ea92fe | ||
|
|
b6b0880f2f | ||
|
|
d4b6d69126 | ||
|
|
75edeab35e | ||
|
|
71d6dad37c | ||
|
|
7f00e1c1a8 | ||
|
|
a39f8e2123 | ||
|
|
2de660fef6 | ||
|
|
dc1be9cf55 | ||
|
|
91e1781dc1 | ||
|
|
e4791c2b7e | ||
|
|
c33e79e0b8 | ||
|
|
5d109b3cd4 | ||
|
|
8074b26bfb | ||
|
|
98c1806369 | ||
|
|
7120d6b35b |
4
.github/workflows/docker-image.yml
vendored
4
.github/workflows/docker-image.yml
vendored
@@ -182,7 +182,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push privately
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
context: .
|
||||
@@ -222,7 +222,7 @@ jobs:
|
||||
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
- name: Build and push privately
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
context: .
|
||||
|
||||
2
.github/workflows/pypi_on_release.yml
vendored
2
.github/workflows/pypi_on_release.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
- name: Build and push publicly
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: "{{defaultContext}}:lsp"
|
||||
push: true
|
||||
|
||||
4
.github/workflows/uffizzi-build.yml
vendored
4
.github/workflows/uffizzi-build.yml
vendored
@@ -25,12 +25,12 @@ jobs:
|
||||
run: echo "UUID_TAG_APP=$(uuidgen)" >> $GITHUB_ENV
|
||||
- name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: registry.uffizzi.com/${{ env.UUID_TAG_APP }}
|
||||
tags: type=raw,value=60d
|
||||
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: true
|
||||
context: ./
|
||||
|
||||
162
CHANGELOG.md
162
CHANGELOG.md
@@ -1,6 +1,168 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [1.70.1](https://github.com/windmill-labs/windmill/compare/v1.70.0...v1.70.1) (2023-02-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** make cli resilient to systems without openable browsers ([c051ffe](https://github.com/windmill-labs/windmill/commit/c051ffeb42c1cff609f93da7745036ea722e17d4))
|
||||
* **frontend:** Disable move in nested subgrid ([#1238](https://github.com/windmill-labs/windmill/issues/1238)) ([70eab30](https://github.com/windmill-labs/windmill/commit/70eab303bd45111ae198d9b710bfd6f9f59e53b0))
|
||||
* **frontend:** Fix inline scripts list ([#1240](https://github.com/windmill-labs/windmill/issues/1240)) ([97602ac](https://github.com/windmill-labs/windmill/commit/97602ac6db1404d36d160a431ffcea6c0f567a48))
|
||||
* **frontend:** Fix subgrid lock ([#1232](https://github.com/windmill-labs/windmill/issues/1232)) ([8ee9d67](https://github.com/windmill-labs/windmill/commit/8ee9d67f4faa91446338b41c664ef91913eb8b81))
|
||||
|
||||
## [1.70.1](https://github.com/windmill-labs/windmill/compare/v1.70.0...v1.70.1) (2023-02-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** make cli resilient to systems without openable browsers ([c051ffe](https://github.com/windmill-labs/windmill/commit/c051ffeb42c1cff609f93da7745036ea722e17d4))
|
||||
* **frontend:** Disable move in nested subgrid ([#1238](https://github.com/windmill-labs/windmill/issues/1238)) ([70eab30](https://github.com/windmill-labs/windmill/commit/70eab303bd45111ae198d9b710bfd6f9f59e53b0))
|
||||
* **frontend:** Fix subgrid lock ([#1232](https://github.com/windmill-labs/windmill/issues/1232)) ([8ee9d67](https://github.com/windmill-labs/windmill/commit/8ee9d67f4faa91446338b41c664ef91913eb8b81))
|
||||
|
||||
## [1.70.0](https://github.com/windmill-labs/windmill/compare/v1.69.3...v1.70.0) (2023-02-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **apps:** add ag grid ([b690d80](https://github.com/windmill-labs/windmill/commit/b690d801d4aa5695ee558e81d1ed114074dfcb83))
|
||||
* **frontend:** move to other grid ([#1230](https://github.com/windmill-labs/windmill/issues/1230)) ([104e4ac](https://github.com/windmill-labs/windmill/commit/104e4ac5e790c30e6fb6b27726776693038d4f19))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* app setup and sync now uses 1.69.3 ([d38aff2](https://github.com/windmill-labs/windmill/commit/d38aff2fe228f23eb18c3991392928c064e6aca2))
|
||||
* **frontend:** Fix duplication ([#1237](https://github.com/windmill-labs/windmill/issues/1237)) ([e87f4fc](https://github.com/windmill-labs/windmill/commit/e87f4fc44b847a573f5acafc0348fbcbfcb2258f))
|
||||
* **frontend:** fix graph viewer id assignment ([e1f686d](https://github.com/windmill-labs/windmill/commit/e1f686d8508cfc1f73c43be08facc44217ca8de0))
|
||||
|
||||
## [1.69.3](https://github.com/windmill-labs/windmill/compare/v1.69.2...v1.69.3) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deno:** fix denoify buffer handling ([c2e5afd](https://github.com/windmill-labs/windmill/commit/c2e5afd4e07fb63375832f308da8c744616ee188))
|
||||
|
||||
## [1.69.2](https://github.com/windmill-labs/windmill/compare/v1.69.1...v1.69.2) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **app:** fix all nested behavior ([dd28308](https://github.com/windmill-labs/windmill/commit/dd28308c3cf1877ba3f19dcd2bd20bf1c7896a99))
|
||||
* **frontend:** delete grid item ([008c30f](https://github.com/windmill-labs/windmill/commit/008c30fcaad64af512407f9889a9881fafac0868))
|
||||
* **frontend:** duplicate ([483407c](https://github.com/windmill-labs/windmill/commit/483407cdf0e1ed61de180a904934e950fed4adc3))
|
||||
* **frontend:** Fix findGridItem ([a8295d0](https://github.com/windmill-labs/windmill/commit/a8295d0b5acd08cec42b7939d907df5c25132644))
|
||||
* **frontend:** Fix findGridItem ([5bb77ed](https://github.com/windmill-labs/windmill/commit/5bb77edf45740a75e969b1bef31580271c9d5505))
|
||||
* **frontend:** Fix next id ([8ddcf4d](https://github.com/windmill-labs/windmill/commit/8ddcf4d9c1a8d6dd20ee241a3f308811c49e58f1))
|
||||
* **frontend:** gridtab ([fa105b4](https://github.com/windmill-labs/windmill/commit/fa105b4caeaa2d0e9704a48f6caf8d846839c23e))
|
||||
* **frontend:** rewrote utils ([ea1b2c2](https://github.com/windmill-labs/windmill/commit/ea1b2c29b95282df347ef9c5973917fa3880e843))
|
||||
* **frontend:** wip ([33ebe2d](https://github.com/windmill-labs/windmill/commit/33ebe2da8e81476be62a2567d5012573a8a010b6))
|
||||
|
||||
## [1.69.1](https://github.com/windmill-labs/windmill/compare/v1.69.0...v1.69.1) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deno:** remove mysql support waiting for deno fix ([dd7e8c7](https://github.com/windmill-labs/windmill/commit/dd7e8c742c83f6a1d13e4343ca626c0b5efc06fb))
|
||||
* **deno:** remove mysql support waiting for deno fix ([2f78132](https://github.com/windmill-labs/windmill/commit/2f78132e081bdf3d7468e022f0e981ebfa52cfb3))
|
||||
* **frontend:** containers and tab fixes v1 ([27cac3f](https://github.com/windmill-labs/windmill/commit/27cac3ffe69c4dac160e9e55ffd1eb8ea348d487))
|
||||
* **frontend:** containers and tab fixes v1 ([705703a](https://github.com/windmill-labs/windmill/commit/705703a5e2f2dc7ceb4c215221f72bf624799841))
|
||||
* **frontend:** containers and tab fixes v1 ([fac31c6](https://github.com/windmill-labs/windmill/commit/fac31c6628b289ad6aae92434e312c4be281a4d2))
|
||||
|
||||
## [1.69.0](https://github.com/windmill-labs/windmill/compare/v1.68.0...v1.69.0) (2023-02-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** Duplicate component ([#1228](https://github.com/windmill-labs/windmill/issues/1228)) ([089a6b6](https://github.com/windmill-labs/windmill/commit/089a6b6ae52e8d28dd15e2f9a6ad900c5853d0a1))
|
||||
* **frontend:** Properly delete tab content ([#1227](https://github.com/windmill-labs/windmill/issues/1227)) ([857ee5f](https://github.com/windmill-labs/windmill/commit/857ee5f318466d12bf0d41515451798df087ab74))
|
||||
* **frontend:** Support deeply nested components ([#1225](https://github.com/windmill-labs/windmill/issues/1225)) ([6ad876e](https://github.com/windmill-labs/windmill/commit/6ad876ebb45a934b7a4dc980cf38a5228d7d11f1))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** .wmillignore whitelist behavior ([d543650](https://github.com/windmill-labs/windmill/commit/d543650b313c434e794ad800aefe4aeda83c0fed))
|
||||
|
||||
## [1.68.0](https://github.com/windmill-labs/windmill/compare/v1.67.4...v1.68.0) (2023-02-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** Add more app component CSS customisation ([#1218](https://github.com/windmill-labs/windmill/issues/1218)) ([6044e3b](https://github.com/windmill-labs/windmill/commit/6044e3b6ef92e89b8f15f38bc2d0986ec64105d5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** better ergonomics around workspace add ([40c12e6](https://github.com/windmill-labs/windmill/commit/40c12e6139c7b42d7ab169bab2dd37f8b43bea06))
|
||||
* **cli:** better ergonomics around workspaces ([3b7160e](https://github.com/windmill-labs/windmill/commit/3b7160e84aa454bdb5f343da99cfd97a6b319937))
|
||||
|
||||
## [1.67.4](https://github.com/windmill-labs/windmill/compare/v1.67.3...v1.67.4) (2023-02-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **backend:** workflow check for has_failure_module ([e54dc3f](https://github.com/windmill-labs/windmill/commit/e54dc3ff97e4454a15b9efe25cc12f6c9e1e176b))
|
||||
|
||||
## [1.67.3](https://github.com/windmill-labs/windmill/compare/v1.67.2...v1.67.3) (2023-02-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** ignone non wmill looking files ([ec57c59](https://github.com/windmill-labs/windmill/commit/ec57c5977f122b629a07e05bc3551662d518ce30))
|
||||
|
||||
## [1.67.2](https://github.com/windmill-labs/windmill/compare/v1.67.1...v1.67.2) (2023-02-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** ignone non wmill looking files ([969e89f](https://github.com/windmill-labs/windmill/commit/969e89f8bbc10f6712920321b70ede35f19ab9ed))
|
||||
|
||||
## [1.67.1](https://github.com/windmill-labs/windmill/compare/v1.67.0...v1.67.1) (2023-02-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** coloring nits ([3fa24ad](https://github.com/windmill-labs/windmill/commit/3fa24adad0a07ba2f469c545b28251b035efdf90))
|
||||
|
||||
## [1.67.0](https://github.com/windmill-labs/windmill/compare/v1.66.1...v1.67.0) (2023-02-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** Add app sub grids ([#1208](https://github.com/windmill-labs/windmill/issues/1208)) ([dbc59e9](https://github.com/windmill-labs/windmill/commit/dbc59e952143ee5813780ad13794cef4e036911c))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** add --fail-conflicts to ci push ([0085b46](https://github.com/windmill-labs/windmill/commit/0085b46c1e3b8267fcafcb06ce72b4d820e49df5))
|
||||
|
||||
## [1.66.1](https://github.com/windmill-labs/windmill/compare/v1.66.0...v1.66.1) (2023-02-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** delete workspace instead of archiving them ([70dfc8b](https://github.com/windmill-labs/windmill/commit/70dfc8b8d0293d80da7db14caa1b9eb0ed67653d))
|
||||
|
||||
## [1.66.0](https://github.com/windmill-labs/windmill/compare/v1.65.0...v1.66.0) (2023-02-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add delete flows ([e81f7bd](https://github.com/windmill-labs/windmill/commit/e81f7bd7239b73710da2a4ddec0da7805c13da06))
|
||||
* CLI refactor v1 ([e31d2ae](https://github.com/windmill-labs/windmill/commit/e31d2ae27f886e774ffc429eea80057f4f9f4213))
|
||||
* **frontend:** Add image app component ([#1213](https://github.com/windmill-labs/windmill/issues/1213)) ([a4b773a](https://github.com/windmill-labs/windmill/commit/a4b773af294554c5787f02ebda363c8d9a3eff1b))
|
||||
|
||||
## [1.65.0](https://github.com/windmill-labs/windmill/compare/v1.64.0...v1.65.0) (2023-02-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **apps:** add asJson for customcss ([71d6dad](https://github.com/windmill-labs/windmill/commit/71d6dad37cc239952ce7799609c02474b0b1fc81))
|
||||
* **apps:** add custom css for apps ([7f00e1c](https://github.com/windmill-labs/windmill/commit/7f00e1c1a8f2e905b0677d82ba547f55dc23b3e0))
|
||||
* **backend:** Zip Workspace Export ([#1201](https://github.com/windmill-labs/windmill/issues/1201)) ([5d109b3](https://github.com/windmill-labs/windmill/commit/5d109b3cd4b7749788f9cb9fcbe1949c45eedf1f))
|
||||
* **frontend:** Add divider app component ([#1209](https://github.com/windmill-labs/windmill/issues/1209)) ([c33e79e](https://github.com/windmill-labs/windmill/commit/c33e79e0b8d5ba1103d87fdd47fcd0e1071e19de))
|
||||
* **frontend:** Add file input app component ([#1211](https://github.com/windmill-labs/windmill/issues/1211)) ([d4b6d69](https://github.com/windmill-labs/windmill/commit/d4b6d691264bf21e4e2c97548aaad9aa80678a6b))
|
||||
* **frontend:** Add icon app component ([#1207](https://github.com/windmill-labs/windmill/issues/1207)) ([e4791c2](https://github.com/windmill-labs/windmill/commit/e4791c2b7e3a0e6b90c37bc1200f9cd0ab3b6845))
|
||||
|
||||
## [1.64.0](https://github.com/windmill-labs/windmill/compare/v1.63.2...v1.64.0) (2023-02-16)
|
||||
|
||||
|
||||
|
||||
43
README.md
43
README.md
@@ -20,40 +20,23 @@ Open-source developer infrastructure for internal tools. Self-hostable alternati
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
<p align="center">
|
||||
<a href="https://app.windmill.dev">Try it</a> - <a href="https://docs.windmill.dev/docs/intro/">Docs</a> - <a href="https://discord.gg/V7PM2YHsPB">Discord</a> - <a href="https://hub.windmill.dev">Hub</a> - <a href="https://docs.windmill.dev/docs/misc/contributing">Contributor's guide</a>
|
||||
</p>
|
||||
|
||||
**Try it (personal workspaces are free forever)**: <https://app.windmill.dev>
|
||||
# Windmill - Turn scripts into workflows and UIs that you can share and run at scale
|
||||
|
||||
**Documentation**: <https://www.windmill.dev>
|
||||
|
||||
**Discord**: <https://discord.gg/V7PM2YHsPB>
|
||||
|
||||
**Hub**: <https://hub.windmill.dev>
|
||||
|
||||
**Contributor's guide**: <https://docs.windmill.dev/docs/misc/contributing>
|
||||
|
||||
**Roadmap**: <https://github.com/orgs/windmill-labs/projects/2>
|
||||
|
||||
You can show your support for the project by starring this repo.
|
||||
|
||||
Windmill Labs offers commercial licenses, an enterprise edition, local hub
|
||||
mirrors, and support: contact ruben@windmill.dev.
|
||||
|
||||
---
|
||||
|
||||
# Windmill
|
||||
|
||||
Windmill is <b>fully open-sourced (AGPLv3)</b>
|
||||
Windmill is <b>fully open-sourced (AGPLv3)</b> and Windmill Labs offers dedicated instance and commercial support and licenses.
|
||||
|
||||

|
||||
|
||||
https://user-images.githubusercontent.com/275584/218350457-bc2fdc3b-e667-4da5-a2bd-3bacc1f0ec79.mp4
|
||||
|
||||
- [Windmill](#windmill)
|
||||
- [Windmill - Turn scripts into workflows and UIs that you can share and run at scale](#windmill---turn-scripts-into-workflows-and-uis-that-you-can-share-and-run-at-scale)
|
||||
- [Main Concepts](#main-concepts)
|
||||
- [Show me some actual script code](#show-me-some-actual-script-code)
|
||||
- [CLI](#cli)
|
||||
- [Layout](#layout)
|
||||
- [Running scripts locally](#running-scripts-locally)
|
||||
- [Stack](#stack)
|
||||
- [Security](#security)
|
||||
- [Sandboxing](#sandboxing)
|
||||
@@ -130,17 +113,15 @@ export async function main(
|
||||
## CLI
|
||||
|
||||
We have a powerful CLI to interact with the windmill platform and sync your
|
||||
scripts from your own github repo. See
|
||||
scripts from local files, github repos and to run scripts and flows on the instance from local commands. See
|
||||
[more details](https://github.com/windmill-labs/windmill/tree/main/cli)
|
||||
|
||||

|
||||
|
||||
## Layout
|
||||
|
||||
- `backend/`: Rust backend
|
||||
- `frontend`: Svelte frontend
|
||||
- `lsp/`: Lsp asssistant for the monaco editor
|
||||
- `<lang>-client/`: Windmill client for the given `<lang>`
|
||||
### Running scripts locally
|
||||
|
||||
You can run your script locally easily, you simply need to pass the right environment variables for the `wmill` client library to fetch resource and variables from your instance if necessary. See more: <https://docs.windmill.dev/docs/advanced/local_development/>
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -386,4 +367,4 @@ running options.
|
||||
|
||||
## Copyright
|
||||
|
||||
Windmill Labs, Inc 2022
|
||||
Windmill Labs, Inc 2023
|
||||
|
||||
292
backend/Cargo.lock
generated
292
backend/Cargo.lock
generated
@@ -12,6 +12,12 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
@@ -104,6 +110,23 @@ dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a"
|
||||
dependencies = [
|
||||
"bzip2",
|
||||
"flate2",
|
||||
"futures-core",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"xz2",
|
||||
"zstd",
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.6.0"
|
||||
@@ -189,6 +212,21 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async_zip"
|
||||
version = "0.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50d29ab7e2f9e808cca1a69ea56a36f4ff216f54a41a23aae1fd4afc05cc020"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"chrono",
|
||||
"crc32fast",
|
||||
"log",
|
||||
"pin-project",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "1.0.0"
|
||||
@@ -217,9 +255,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.6"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e246206a63c9830e118d12c894f56a82033da1a2361f5544deeee3df85c99d9"
|
||||
checksum = "6137c6234afb339e75e764c866e3594900f0211e1315d33779f269bbe2ec6967"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
@@ -286,9 +324,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.5.3"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "better_scoped_tls"
|
||||
@@ -412,6 +450,27 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
|
||||
dependencies = [
|
||||
"bzip2-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bzip2-sys"
|
||||
version = "0.1.11+1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-lock"
|
||||
version = "8.0.3"
|
||||
@@ -494,9 +553,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
|
||||
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
@@ -522,9 +581,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b"
|
||||
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
@@ -560,7 +619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time 0.3.17",
|
||||
"time 0.3.20",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
@@ -613,6 +672,15 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cron"
|
||||
version = "0.12.0"
|
||||
@@ -919,9 +987,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
|
||||
checksum = "b2e5d13ca2353ab7d0230988629def93914a8c4015f621f9b13ed2955614731d"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
@@ -1001,6 +1069,16 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -1374,9 +1452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
||||
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -1560,9 +1638,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
|
||||
|
||||
[[package]]
|
||||
name = "is-macro"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c068d4c6b922cd6284c609cfa6dec0e41615c9c5a1a4ba729a970d8daba05fb"
|
||||
checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"pmutil",
|
||||
@@ -1573,9 +1651,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
|
||||
checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
@@ -1824,6 +1902,17 @@ dependencies = [
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzma-sys"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magic-crypt"
|
||||
version = "3.1.12"
|
||||
@@ -1904,6 +1993,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.6"
|
||||
@@ -2051,18 +2149,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.9"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
|
||||
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.9"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
|
||||
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -2468,7 +2566,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#be803fafa129c7b50c652f043ae85cf2db872b80"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"built",
|
||||
@@ -2486,7 +2584,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-client"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#be803fafa129c7b50c652f043ae85cf2db872b80"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -2500,7 +2598,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-impl"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#be803fafa129c7b50c652f043ae85cf2db872b80"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"heck",
|
||||
@@ -2522,7 +2620,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-macro"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#be803fafa129c7b50c652f043ae85cf2db872b80"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#82c979df65476fe4dfc2590970ccdf64113e9e0c"
|
||||
dependencies = [
|
||||
"openapiv3",
|
||||
"proc-macro2",
|
||||
@@ -2700,15 +2798,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.14"
|
||||
@@ -2905,7 +2994,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#3c4f04f3aa457d386815b94bfc17564ad40cbc89"
|
||||
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-compiler-core",
|
||||
@@ -2914,7 +3003,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#3c4f04f3aa457d386815b94bfc17564ad40cbc89"
|
||||
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2931,7 +3020,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#3c4f04f3aa457d386815b94bfc17564ad40cbc89"
|
||||
source = "git+https://github.com/RustPython/RustPython#351d464448607413dc12de6905f1165f8d45af54"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2985,9 +3074,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.11"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
|
||||
checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dyn-clone",
|
||||
@@ -2999,9 +3088,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.11"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
|
||||
checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3317,9 +3406,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
|
||||
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -3599,9 +3688,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "swc_atoms"
|
||||
version = "0.4.36"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10ad195f903dd49e76fd93bc02c4d5fdb34287f9847f73d26c2097090db7cac3"
|
||||
checksum = "f88175a66f5a7c189e752bda520e148317776ecb22c75adc2c2f24c490834bd0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustc-hash",
|
||||
@@ -3613,9 +3702,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_common"
|
||||
version = "0.29.31"
|
||||
version = "0.29.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c967c1c2f17fac8969831752560289e377712091003bdd1af6f025f2d70dc2"
|
||||
checksum = "1fc8e0e8109b26be70c82d9709562fc88cbcc09e03c2458221cf216c0088dea2"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ast_node",
|
||||
@@ -3640,9 +3729,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_ast"
|
||||
version = "0.96.5"
|
||||
version = "0.96.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84192270c26fc125494ee8895baabf480a95ef8ef83fefb79a94d9fe90c5eb1b"
|
||||
checksum = "621c66e27fbb6cbb6434a4e2b25e439e9a2583cc3419a4a83eba51d16ac0cd7b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"is-macro",
|
||||
@@ -3657,9 +3746,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_parser"
|
||||
version = "0.124.9"
|
||||
version = "0.124.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd431de25e6c311ffa7dd4d01907d602e5e32311dc5d9bd73cc0993b15a4c069"
|
||||
checksum = "89b3f472b3dfbfd279de364d2b014459a281824b938e243a8739037c445d6b6c"
|
||||
dependencies = [
|
||||
"either",
|
||||
"enum_kind",
|
||||
@@ -3725,9 +3814,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3742,16 +3831,15 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
"rustix",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3828,9 +3916,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.17"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
@@ -3846,9 +3934,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.6"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||
checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
@@ -3943,9 +4031,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
|
||||
checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@@ -4055,9 +4143,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
|
||||
checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
@@ -4202,7 +4290,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
[[package]]
|
||||
name = "typify"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d0c9a1c85a9e04ad5937c8df9ff2521b8ea91ea6"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
|
||||
dependencies = [
|
||||
"typify-impl",
|
||||
"typify-macro",
|
||||
@@ -4211,7 +4299,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-impl"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d0c9a1c85a9e04ad5937c8df9ff2521b8ea91ea6"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
@@ -4229,7 +4317,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-macro"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d0c9a1c85a9e04ad5937c8df9ff2521b8ea91ea6"
|
||||
source = "git+https://github.com/oxidecomputer/typify#d579a526b3cc2e0c36d17fae8df549a03187f177"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4668,7 +4756,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4695,12 +4783,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
"async-oauth2",
|
||||
"async-stripe",
|
||||
"async_zip",
|
||||
"axum",
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
@@ -4727,7 +4816,7 @@ dependencies = [
|
||||
"sql-builder",
|
||||
"sqlx",
|
||||
"tempfile",
|
||||
"time 0.3.17",
|
||||
"time 0.3.20",
|
||||
"tokio",
|
||||
"tokio-tar",
|
||||
"tokio-util",
|
||||
@@ -4749,7 +4838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
@@ -4764,7 +4853,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -4777,7 +4866,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4802,7 +4891,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4810,7 +4899,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4824,7 +4913,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4836,7 +4925,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@@ -4851,7 +4940,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deno_core",
|
||||
@@ -4865,7 +4954,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -4888,7 +4977,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -5020,8 +5109,47 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xz2"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
|
||||
dependencies = [
|
||||
"lzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.11.2+zstd.1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "5.0.2+zstd.1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.7+zstd.1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.64.0"
|
||||
version = "1.70.1"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -28,7 +28,11 @@ name = "windmill"
|
||||
path = "./src/main.rs"
|
||||
|
||||
[features]
|
||||
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise"]
|
||||
enterprise = [
|
||||
"windmill-worker/enterprise",
|
||||
"windmill-queue/enterprise",
|
||||
"windmill-api/enterprise",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@@ -144,4 +148,8 @@ serde_derive = "1.0.147"
|
||||
const_format = { version = "0.2", features = ["rust_1_64", "rust_1_51"] }
|
||||
dyn-iter = "0.2.0"
|
||||
rsa = "0.7.2"
|
||||
async-stripe = { version = "0.14", features = ["runtime-tokio-hyper", "checkout"] }
|
||||
async-stripe = { version = "0.14", features = [
|
||||
"runtime-tokio-hyper",
|
||||
"checkout",
|
||||
] }
|
||||
async_zip = { version = "0.0.11", features = ["full"] }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Add up migration script here
|
||||
|
||||
UPDATE script SET content = 'import wmill from "https://deno.land/x/wmill@v1.70.1/main.ts";
|
||||
export async function main() {
|
||||
await run(
|
||||
"workspace", "add", "__automation", "admins", Deno.env.get("BASE_INTERNAL_URL") + "/", "--token", Deno.env.get("WM_TOKEN"));
|
||||
|
||||
await run("hub", "pull");
|
||||
}
|
||||
|
||||
async function run(...cmd: string[]) {
|
||||
console.log("Running \"" + cmd.join('' '') + "\"");
|
||||
await wmill.parse(cmd);
|
||||
}', summary = 'Synchronize Hub Resource types with admins workspace',
|
||||
description = 'Basic administrative script to sync latest resource types from hub to share to every workspace. Recommended to run at least once. On a schedule by default.'
|
||||
WHERE hash = -28028598712388162 AND workspace_id = 'admins';
|
||||
@@ -17,10 +17,8 @@ use serde_json::json;
|
||||
use windmill_common::error;
|
||||
use windmill_parser::{json_to_typ, Arg, MainArgSignature, Typ};
|
||||
|
||||
use rustpython_parser::{
|
||||
ast::{Constant, ExprKind, Located, StmtKind},
|
||||
parser,
|
||||
};
|
||||
use rustpython_parser as parser;
|
||||
use rustpython_parser::ast::{Constant, ExprKind, Located, StmtKind};
|
||||
|
||||
const DEF_MAIN: &str = "def main(";
|
||||
const FUNCTION_CALL: &str = "<function call>";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ name = "windmill_api"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
enterprise = ["windmill-queue/enterprise"]
|
||||
enterprise = ["windmill-queue/enterprise", "async-stripe"]
|
||||
|
||||
[dependencies]
|
||||
windmill-queue.workspace = true
|
||||
@@ -65,6 +65,7 @@ hmac.workspace = true
|
||||
cookie.workspace = true
|
||||
sha2.workspace = true
|
||||
urlencoding.workspace = true
|
||||
async-stripe.workspace = true
|
||||
async-stripe = { workspace = true, optional = true }
|
||||
lazy_static.workspace = true
|
||||
prometheus.workspace = true
|
||||
prometheus.workspace = true
|
||||
async_zip.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.64.0
|
||||
version: 1.70.1
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -1092,6 +1092,10 @@ paths:
|
||||
- variable
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- name: already_encrypted
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
requestBody:
|
||||
description: new variable
|
||||
required: true
|
||||
@@ -1133,6 +1137,10 @@ paths:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
- name: already_encrypted
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
requestBody:
|
||||
description: updated variable
|
||||
required: true
|
||||
@@ -2708,6 +2716,23 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/flows/delete/{path}:
|
||||
delete:
|
||||
summary: delete flow by path
|
||||
operationId: deleteFlowByPath
|
||||
tags:
|
||||
- flow
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: flow delete
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/list:
|
||||
get:
|
||||
summary: list all available apps
|
||||
|
||||
@@ -170,7 +170,7 @@ async fn list_apps(
|
||||
)
|
||||
.order_desc("favorite.path IS NOT NULL")
|
||||
.order_by("app_version.created_at", true)
|
||||
.and_where("app.workspace_id = ? OR app.workspace_id = 'starter'".bind(&w_id))
|
||||
.and_where("app.workspace_id = ?".bind(&w_id))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
|
||||
@@ -11,7 +11,7 @@ use sql_builder::prelude::*;
|
||||
|
||||
use axum::{
|
||||
extract::{Extension, Path, Query},
|
||||
routing::{get, post},
|
||||
routing::{delete, get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use sql_builder::SqlBuilder;
|
||||
@@ -41,6 +41,7 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/create", post(create_flow))
|
||||
.route("/update/*path", post(update_flow))
|
||||
.route("/archive/*path", post(archive_flow_by_path))
|
||||
.route("/delete/*path", delete(delete_flow_by_path))
|
||||
.route("/get/*path", get(get_flow_by_path))
|
||||
.route("/exists/*path", get(exists_flow_by_path))
|
||||
.route("/list_paths", get(list_paths))
|
||||
@@ -81,7 +82,7 @@ async fn list_flows(
|
||||
)
|
||||
.order_desc("favorite.path IS NOT NULL")
|
||||
.order_by("edited_at", lq.order_desc.unwrap_or(true))
|
||||
.and_where("o.workspace_id = ? OR o.workspace_id = 'starter'".bind(&w_id))
|
||||
.and_where("o.workspace_id = ?".bind(&w_id))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
@@ -426,13 +427,12 @@ async fn get_flow_by_path(
|
||||
let path = path.to_path();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let flow_o = sqlx::query_as::<_, Flow>(
|
||||
"SELECT * FROM flow WHERE path = $1 AND (workspace_id = $2 OR workspace_id = 'starter')",
|
||||
)
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?;
|
||||
let flow_o =
|
||||
sqlx::query_as::<_, Flow>("SELECT * FROM flow WHERE path = $1 AND workspace_id = $2")
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
let flow = not_found_if_none(flow_o, "Flow", path)?;
|
||||
@@ -446,8 +446,7 @@ async fn exists_flow_by_path(
|
||||
let path = path.to_path();
|
||||
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM flow WHERE path = $1 AND (workspace_id = $2 OR workspace_id \
|
||||
= 'starter'))",
|
||||
"SELECT EXISTS(SELECT 1 FROM flow WHERE path = $1 AND workspace_id = $2)",
|
||||
path,
|
||||
w_id
|
||||
)
|
||||
@@ -494,6 +493,42 @@ async fn archive_flow_by_path(
|
||||
Ok(format!("Flow {path} archived"))
|
||||
}
|
||||
|
||||
async fn delete_flow_by_path(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> Result<String> {
|
||||
let path = path.to_path();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
path,
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut tx,
|
||||
&authed.username,
|
||||
"flows.delete",
|
||||
ActionKind::Delete,
|
||||
&w_id,
|
||||
Some(path),
|
||||
Some([("workspace", w_id.as_str())].into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::DeleteFlow { workspace: w_id, path: path.to_owned() },
|
||||
);
|
||||
|
||||
Ok(format!("Flow {path} deleted"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
||||
@@ -152,8 +152,7 @@ pub async fn get_path_for_hash<'c>(
|
||||
hash: i64,
|
||||
) -> error::Result<String> {
|
||||
let path = sqlx::query_scalar!(
|
||||
"select path from script where hash = $1 AND (workspace_id = $2 OR workspace_id = \
|
||||
'starter')",
|
||||
"select path from script where hash = $1 AND workspace_id = $2",
|
||||
hash,
|
||||
w_id
|
||||
)
|
||||
|
||||
@@ -142,7 +142,7 @@ async fn list_resources(
|
||||
.join("account")
|
||||
.on("variable.account = account.id AND account.workspace_id = variable.workspace_id")
|
||||
.order_by("path", true)
|
||||
.and_where("resource.workspace_id = ? OR resource.workspace_id = 'starter'".bind(&w_id))
|
||||
.and_where("resource.workspace_id = ?".bind(&w_id))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
@@ -187,7 +187,7 @@ async fn get_resource(
|
||||
FROM resource
|
||||
LEFT JOIN variable ON variable.path = resource.path AND variable.workspace_id = resource.workspace_id
|
||||
LEFT JOIN account ON variable.account = account.id AND account.workspace_id = resource.workspace_id
|
||||
WHERE resource.path = $1 AND (resource.workspace_id = $2 OR resource.workspace_id = 'starter')",
|
||||
WHERE resource.path = $1 AND resource.workspace_id = $2",
|
||||
path.to_owned(),
|
||||
&w_id
|
||||
)
|
||||
@@ -226,8 +226,7 @@ async fn get_resource_value(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let value_o = sqlx::query_scalar!(
|
||||
"SELECT value from resource WHERE path = $1 AND (workspace_id = $2 OR workspace_id = \
|
||||
'starter')",
|
||||
"SELECT value from resource WHERE path = $1 AND workspace_id = $2",
|
||||
path.to_owned(),
|
||||
&w_id
|
||||
)
|
||||
@@ -478,7 +477,7 @@ async fn list_resource_types(
|
||||
) -> JsonResult<Vec<ResourceType>> {
|
||||
let rows = sqlx::query_as!(
|
||||
ResourceType,
|
||||
"SELECT * from resource_type WHERE (workspace_id = $1 OR workspace_id = 'starter' OR workspace_id = 'admins') ORDER \
|
||||
"SELECT * from resource_type WHERE (workspace_id = $1 OR workspace_id = 'admins') ORDER \
|
||||
BY name",
|
||||
&w_id
|
||||
)
|
||||
@@ -493,7 +492,7 @@ async fn list_resource_types_names(
|
||||
Path(w_id): Path<String>,
|
||||
) -> JsonResult<Vec<String>> {
|
||||
let rows = sqlx::query_scalar!(
|
||||
"SELECT name from resource_type WHERE (workspace_id = $1 OR workspace_id = 'starter' OR workspace_id = 'admins') \
|
||||
"SELECT name from resource_type WHERE (workspace_id = $1 OR workspace_id = 'admins') \
|
||||
ORDER BY name",
|
||||
&w_id
|
||||
)
|
||||
@@ -512,8 +511,7 @@ async fn get_resource_type(
|
||||
|
||||
let resource_type_o = sqlx::query_as!(
|
||||
ResourceType,
|
||||
"SELECT * from resource_type WHERE name = $1 AND (workspace_id = $2 OR workspace_id = \
|
||||
'starter' OR workspace_id = 'admins')",
|
||||
"SELECT * from resource_type WHERE name = $1 AND (workspace_id = $2 OR workspace_id = 'admins')",
|
||||
&name,
|
||||
&w_id
|
||||
)
|
||||
@@ -530,8 +528,7 @@ async fn exists_resource_type(
|
||||
Path((w_id, name)): Path<(String, String)>,
|
||||
) -> JsonResult<bool> {
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM resource_type WHERE name = $1 AND (workspace_id = $2 OR workspace_id = \
|
||||
'starter' OR workspace_id = 'admins'))",
|
||||
"SELECT EXISTS(SELECT 1 FROM resource_type WHERE name = $1 AND (workspace_id = $2 OR workspace_id = 'admins'))",
|
||||
name,
|
||||
w_id
|
||||
)
|
||||
|
||||
@@ -111,7 +111,7 @@ async fn list_scripts(
|
||||
)
|
||||
.order_desc("favorite.path IS NOT NULL")
|
||||
.order_by("created_at", lq.order_desc.unwrap_or(true))
|
||||
.and_where("o.workspace_id = ? OR o.workspace_id = 'starter'".bind(&w_id))
|
||||
.and_where("o.workspace_id = ?".bind(&w_id))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
@@ -120,7 +120,7 @@ async fn list_scripts(
|
||||
sqlb.and_where_eq(
|
||||
"created_at",
|
||||
"(select max(created_at) from script where o.path = path
|
||||
AND (workspace_id = ? OR workspace_id = 'starter'))"
|
||||
AND workspace_id = ?)"
|
||||
.bind(&w_id),
|
||||
);
|
||||
} else {
|
||||
@@ -461,9 +461,9 @@ async fn get_script_by_path(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let script_o = sqlx::query_as::<_, Script>(
|
||||
"SELECT * FROM script WHERE path = $1 AND (workspace_id = $2 OR workspace_id = 'starter') \
|
||||
"SELECT * FROM script WHERE path = $1 AND workspace_id = $2 \
|
||||
AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \
|
||||
(workspace_id = $2 OR workspace_id = 'starter'))",
|
||||
workspace_id = $2)",
|
||||
)
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
@@ -502,11 +502,12 @@ async fn raw_script_by_path(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let content_o = sqlx::query_scalar!(
|
||||
"SELECT content FROM script WHERE path = $1 AND (workspace_id = $2 OR workspace_id = 'starter') \
|
||||
"SELECT content FROM script WHERE path = $1 AND workspace_id = $2 \
|
||||
AND
|
||||
created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND archived = false AND \
|
||||
(workspace_id = $2 OR workspace_id = 'starter'))",
|
||||
path, w_id
|
||||
workspace_id = $2)",
|
||||
path,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?;
|
||||
@@ -523,10 +524,8 @@ async fn exists_script_by_path(
|
||||
let path = path.to_path();
|
||||
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND (workspace_id = $2 OR \
|
||||
workspace_id = 'starter') AND
|
||||
created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND (workspace_id = $2 \
|
||||
OR workspace_id = 'starter')))",
|
||||
"SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND
|
||||
created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2))",
|
||||
path,
|
||||
w_id
|
||||
)
|
||||
@@ -542,13 +541,12 @@ async fn get_script_by_hash_internal<'c>(
|
||||
workspace_id: &str,
|
||||
hash: &ScriptHash,
|
||||
) -> Result<Script> {
|
||||
let script_o = sqlx::query_as::<_, Script>(
|
||||
"SELECT * FROM script WHERE hash = $1 AND (workspace_id = $2 OR workspace_id = 'starter')",
|
||||
)
|
||||
.bind(hash)
|
||||
.bind(workspace_id)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
let script_o =
|
||||
sqlx::query_as::<_, Script>("SELECT * FROM script WHERE hash = $1 AND workspace_id = $2")
|
||||
.bind(hash)
|
||||
.bind(workspace_id)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
|
||||
let script = not_found_if_none(script_o, "Script", hash.to_string())?;
|
||||
Ok(script)
|
||||
@@ -594,8 +592,7 @@ async fn get_deployment_status(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let status_o: Option<DeploymentStatus> = sqlx::query_as!(
|
||||
DeploymentStatus,
|
||||
"SELECT lock, lock_error_logs FROM script WHERE hash = $1 AND (workspace_id = $2 OR \
|
||||
workspace_id = 'starter')",
|
||||
"SELECT lock, lock_error_logs FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
hash.0,
|
||||
w_id,
|
||||
)
|
||||
|
||||
@@ -85,7 +85,7 @@ async fn list_variables(
|
||||
from variable
|
||||
LEFT JOIN account ON variable.account = account.id AND account.workspace_id = variable.workspace_id
|
||||
LEFT JOIN resource ON resource.path = variable.path AND resource.workspace_id = variable.workspace_id
|
||||
WHERE variable.workspace_id = $1 OR (is_secret IS NOT TRUE AND variable.workspace_id = 'starter') ORDER BY path",
|
||||
WHERE variable.workspace_id = $1 ORDER BY path",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.fetch_all(&mut tx)
|
||||
@@ -116,8 +116,7 @@ async fn get_variable(
|
||||
from variable
|
||||
LEFT JOIN account ON variable.account = account.id
|
||||
LEFT JOIN resource ON resource.path = variable.path AND resource.workspace_id = variable.workspace_id
|
||||
WHERE variable.path = $1 AND (variable.workspace_id = $2 OR (is_secret IS NOT TRUE AND \
|
||||
variable.workspace_id = 'starter'))
|
||||
WHERE variable.path = $1 AND variable.workspace_id = $2
|
||||
LIMIT 1",
|
||||
)
|
||||
.bind(&path)
|
||||
@@ -209,12 +208,13 @@ async fn create_variable(
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(AlreadyEncrypted { already_encrypted }): Query<AlreadyEncrypted>,
|
||||
Json(variable): Json<CreateVariable>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
check_path_conflict(&mut tx, &w_id, &variable.path).await?;
|
||||
let value = if variable.is_secret {
|
||||
let value = if variable.is_secret && !already_encrypted.unwrap_or(false) {
|
||||
let mc = build_crypt(&mut tx, &w_id).await?;
|
||||
encrypt(&mc, &variable.value)
|
||||
} else {
|
||||
@@ -312,12 +312,18 @@ struct EditVariable {
|
||||
description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AlreadyEncrypted {
|
||||
already_encrypted: Option<bool>,
|
||||
}
|
||||
|
||||
async fn update_variable(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Query(AlreadyEncrypted { already_encrypted }): Query<AlreadyEncrypted>,
|
||||
Json(ns): Json<EditVariable>,
|
||||
) -> Result<String> {
|
||||
use sql_builder::prelude::*;
|
||||
@@ -343,7 +349,7 @@ async fn update_variable(
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
|
||||
let value = if is_secret {
|
||||
let value = if is_secret && !already_encrypted.unwrap_or(false) {
|
||||
let mc = build_crypt(&mut tx, &w_id).await?;
|
||||
encrypt(&mc, &nvalue)
|
||||
} else {
|
||||
|
||||
@@ -24,6 +24,7 @@ pub enum WebhookMessage {
|
||||
CreateFlow { workspace: String, path: String },
|
||||
UpdateFlow { workspace: String, old_path: String, new_path: String },
|
||||
ArchiveFlow { workspace: String, path: String },
|
||||
DeleteFlow { workspace: String, path: String },
|
||||
CreateFolder { workspace: String, name: String },
|
||||
UpdateFolder { workspace: String, name: String },
|
||||
DeleteFolder { workspace: String, name: String },
|
||||
|
||||
@@ -6,24 +6,31 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::BASE_URL;
|
||||
use crate::{
|
||||
apps::AppWithLastVersion,
|
||||
db::{UserDB, DB},
|
||||
folders::Folder,
|
||||
resources::{Resource, ResourceType},
|
||||
users::{Authed, WorkspaceInvite, NEW_USER_WEBHOOK},
|
||||
utils::require_super_admin,
|
||||
BASE_URL, HTTP_CLIENT,
|
||||
HTTP_CLIENT,
|
||||
};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use axum::response::Redirect;
|
||||
use axum::{
|
||||
body::StreamBody,
|
||||
extract::{Extension, Path, Query},
|
||||
headers,
|
||||
response::{IntoResponse, Redirect},
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, post},
|
||||
Json, Router,
|
||||
};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use stripe::CustomerId;
|
||||
use windmill_audit::{audit_log, ActionKind};
|
||||
use windmill_common::{
|
||||
@@ -42,7 +49,7 @@ use tokio::fs::File;
|
||||
use tokio_util::io::ReaderStream;
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
let router = Router::new()
|
||||
.route("/list_pending_invites", get(list_pending_invites))
|
||||
.route("/update", post(edit_workspace))
|
||||
.route("/archive", post(archive_workspace))
|
||||
@@ -54,9 +61,17 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/edit_webhook", post(edit_webhook))
|
||||
.route("/edit_auto_invite", post(edit_auto_invite))
|
||||
.route("/tarball", get(tarball_workspace))
|
||||
.route("/premium_info", get(premium_info))
|
||||
.route("/premium_info", get(premium_info));
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
tracing::info!("stripe enabled");
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
let router = router
|
||||
.route("/checkout", get(stripe_checkout))
|
||||
.route("/billing_portal", get(stripe_portal))
|
||||
.route("/billing_portal", get(stripe_portal));
|
||||
|
||||
router
|
||||
}
|
||||
pub fn global_service() -> Router {
|
||||
Router::new()
|
||||
@@ -216,11 +231,13 @@ async fn premium_info(
|
||||
Ok(Json(row))
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
#[derive(Deserialize)]
|
||||
struct PlanQuery {
|
||||
plan: String,
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
async fn stripe_checkout(
|
||||
authed: Authed,
|
||||
Path(w_id): Path<String>,
|
||||
@@ -286,6 +303,7 @@ async fn stripe_checkout(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
async fn stripe_portal(
|
||||
authed: Authed,
|
||||
Path(w_id): Path<String>,
|
||||
@@ -659,19 +677,19 @@ async fn create_workspace(
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
|
||||
let mc = magic_crypt::new_magic_crypt!(key, 256);
|
||||
sqlx::query!(
|
||||
"INSERT INTO variable
|
||||
(workspace_id, path, value, is_secret, description)
|
||||
VALUES ($1, 'g/all/pretty_secret', $2, true, 'This item is secret'),
|
||||
($3, 'g/all/not_secret', $4, false, 'This item is not secret')",
|
||||
nw.id,
|
||||
crate::variables::encrypt(&mc, "pretty secret value"),
|
||||
nw.id,
|
||||
"finland does not actually exist",
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
// let mc = magic_crypt::new_magic_crypt!(key, 256);
|
||||
// sqlx::query!(
|
||||
// "INSERT INTO variable
|
||||
// (workspace_id, path, value, is_secret, description)
|
||||
// VALUES ($1, 'g/all/pretty_secret', $2, true, 'This item is secret'),
|
||||
// ($3, 'g/all/not_secret', $4, false, 'This item is not secret')",
|
||||
// nw.id,
|
||||
// crate::variables::encrypt(&mc, "pretty secret value"),
|
||||
// nw.id,
|
||||
// "finland does not actually exist",
|
||||
// )
|
||||
// .execute(&mut tx)
|
||||
// .await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO usr
|
||||
@@ -1048,20 +1066,118 @@ struct ScriptMetadata {
|
||||
lock: Vec<String>,
|
||||
}
|
||||
|
||||
enum ArchiveImpl {
|
||||
Zip(async_zip::write::ZipFileWriter<File>),
|
||||
Tar(tokio_tar::Builder<File>),
|
||||
}
|
||||
|
||||
impl ArchiveImpl {
|
||||
async fn write_to_archive(&mut self, content: &str, path: &str) -> Result<()> {
|
||||
match self {
|
||||
ArchiveImpl::Tar(t) => {
|
||||
let bytes = content.as_bytes();
|
||||
let mut header = tokio_tar::Header::new_gnu();
|
||||
header.set_size(bytes.len() as u64);
|
||||
header.set_mtime(0);
|
||||
header.set_uid(0);
|
||||
header.set_gid(0);
|
||||
header.set_mode(0o777);
|
||||
header.set_cksum();
|
||||
t.append_data(&mut header, path, bytes).await?;
|
||||
}
|
||||
ArchiveImpl::Zip(z) => {
|
||||
let header = async_zip::ZipEntryBuilder::new(
|
||||
path.to_owned(),
|
||||
async_zip::Compression::Deflate,
|
||||
)
|
||||
.last_modification_date(Default::default())
|
||||
.unix_permissions(0o777)
|
||||
.build();
|
||||
z.write_entry_whole(header, content.as_bytes())
|
||||
.await
|
||||
.map_err(to_anyhow)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn finish(self) -> Result<()> {
|
||||
match self {
|
||||
ArchiveImpl::Tar(t) => t.into_inner().await?,
|
||||
ArchiveImpl::Zip(z) => z.close().await.map_err(to_anyhow)?,
|
||||
}
|
||||
.sync_all()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ArchiveQueryParams {
|
||||
archive_type: Option<String>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_string_without_metadata<T>(value: &T, preserve_extra_perms: bool) -> Result<String>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let value = serde_json::to_value(value).map_err(to_anyhow)?;
|
||||
value
|
||||
.as_object()
|
||||
.map(|obj| {
|
||||
let mut obj = obj.clone();
|
||||
for key in [
|
||||
"workspace_id",
|
||||
"path",
|
||||
"name",
|
||||
"versions",
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"edited_at",
|
||||
"edited_by",
|
||||
"archived",
|
||||
] {
|
||||
if obj.contains_key(key) {
|
||||
obj.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
if !preserve_extra_perms && obj.contains_key("extra_perms") {
|
||||
obj.remove("extra_perms");
|
||||
}
|
||||
|
||||
serde_json::to_string_pretty(&obj).ok()
|
||||
})
|
||||
.flatten()
|
||||
.ok_or_else(|| Error::BadRequest("Impossible to serialize value".to_string()))
|
||||
}
|
||||
|
||||
async fn tarball_workspace(
|
||||
authed: Authed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(ArchiveQueryParams { archive_type }): Query<ArchiveQueryParams>,
|
||||
) -> Result<([(headers::HeaderName, String); 2], impl IntoResponse)> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
let tmp_dir = TempDir::new_in(".")?;
|
||||
|
||||
let name = format!("windmill-{w_id}.tar");
|
||||
let name = match archive_type.as_deref() {
|
||||
Some("tar") | None => Ok(format!("windmill-{w_id}.tar")),
|
||||
Some("zip") => Ok(format!("windmill-{w_id}.zip")),
|
||||
Some(t) => Err(Error::BadRequest(format!("Invalid Archive Type {t}"))),
|
||||
}?;
|
||||
let file_path = tmp_dir.path().join(&name);
|
||||
let file = File::create(&file_path).await?;
|
||||
let mut a = tokio_tar::Builder::new(file);
|
||||
|
||||
let mut archive = match archive_type.as_deref() {
|
||||
Some("tar") | None => Ok(ArchiveImpl::Tar(tokio_tar::Builder::new(file))),
|
||||
Some("zip") => Ok(ArchiveImpl::Zip(async_zip::write::ZipFileWriter::new(file))),
|
||||
Some(t) => Err(Error::BadRequest(format!("Invalid Archive Type {t}"))),
|
||||
}?;
|
||||
{
|
||||
let folders = sqlx::query_as::<_, Folder>("SELECT * FROM folder WHERE workspace_id = $1")
|
||||
.bind(&w_id)
|
||||
@@ -1069,12 +1185,12 @@ async fn tarball_workspace(
|
||||
.await?;
|
||||
|
||||
for folder in folders {
|
||||
write_to_archive(
|
||||
serde_json::to_string_pretty(&folder).unwrap(),
|
||||
format!("f/{}/folder.meta.json", folder.name),
|
||||
&mut a,
|
||||
)
|
||||
.await?;
|
||||
archive
|
||||
.write_to_archive(
|
||||
&to_string_without_metadata(&folder, true).unwrap(),
|
||||
&format!("f/{}/folder.meta.json", folder.name),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1095,7 +1211,9 @@ async fn tarball_workspace(
|
||||
ScriptLang::Go => "go",
|
||||
ScriptLang::Bash => "sh",
|
||||
};
|
||||
write_to_archive(script.content, format!("{}.{}", script.path, ext), &mut a).await?;
|
||||
archive
|
||||
.write_to_archive(&script.content, &format!("{}.{}", script.path, ext))
|
||||
.await?;
|
||||
|
||||
let lock = script
|
||||
.lock
|
||||
@@ -1111,7 +1229,9 @@ async fn tarball_workspace(
|
||||
lock,
|
||||
};
|
||||
let metadata_str = serde_json::to_string_pretty(&metadata).unwrap();
|
||||
write_to_archive(metadata_str, format!("{}.script.json", script.path), &mut a).await?;
|
||||
archive
|
||||
.write_to_archive(&metadata_str, &format!("{}.script.json", script.path))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,13 +1245,10 @@ async fn tarball_workspace(
|
||||
.await?;
|
||||
|
||||
for resource in resources {
|
||||
let resource_str = serde_json::to_string_pretty(&resource).unwrap();
|
||||
write_to_archive(
|
||||
resource_str,
|
||||
format!("{}.resource.json", resource.path),
|
||||
&mut a,
|
||||
)
|
||||
.await?;
|
||||
let resource_str = &to_string_without_metadata(&resource, false).unwrap();
|
||||
archive
|
||||
.write_to_archive(&resource_str, &format!("{}.resource.json", resource.path))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1145,13 +1262,13 @@ async fn tarball_workspace(
|
||||
.await?;
|
||||
|
||||
for resource_type in resource_types {
|
||||
let resource_str = serde_json::to_string_pretty(&resource_type).unwrap();
|
||||
write_to_archive(
|
||||
resource_str,
|
||||
format!("{}.resource-type.json", resource_type.name),
|
||||
&mut a,
|
||||
)
|
||||
.await?;
|
||||
let resource_str = &to_string_without_metadata(&resource_type, false).unwrap();
|
||||
archive
|
||||
.write_to_archive(
|
||||
&resource_str,
|
||||
&format!("{}.resource-type.json", resource_type.name),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1164,25 +1281,49 @@ async fn tarball_workspace(
|
||||
.await?;
|
||||
|
||||
for flow in flows {
|
||||
let flow_str = serde_json::to_string_pretty(&flow).unwrap();
|
||||
write_to_archive(flow_str, format!("{}.flow.json", flow.path), &mut a).await?;
|
||||
let flow_str = &to_string_without_metadata(&flow, false).unwrap();
|
||||
archive
|
||||
.write_to_archive(&flow_str, &format!("{}.flow.json", flow.path))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let variables = sqlx::query_as::<_, ExportableListableVariable>(
|
||||
"SELECT *, false as is_expired FROM variable WHERE workspace_id = $1 AND is_secret = false",
|
||||
"SELECT *, false as is_expired FROM variable WHERE workspace_id = $1",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
|
||||
for var in variables {
|
||||
let flow_str = serde_json::to_string_pretty(&var).unwrap();
|
||||
write_to_archive(flow_str, format!("{}.variable.json", var.path), &mut a).await?;
|
||||
let var_str = &to_string_without_metadata(&var, false).unwrap();
|
||||
archive
|
||||
.write_to_archive(&var_str, &format!("{}.variable.json", var.path))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
a.into_inner().await?;
|
||||
|
||||
{
|
||||
let apps = sqlx::query_as!(
|
||||
AppWithLastVersion,
|
||||
"SELECT app.id, app.path, app.summary, app.versions, app.policy,
|
||||
app.extra_perms, app_version.value,
|
||||
app_version.created_at, app_version.created_by from app, app_version
|
||||
WHERE app.workspace_id = $1 AND app_version.id = app.versions[array_upper(app.versions, 1)]",
|
||||
&w_id
|
||||
)
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
|
||||
for app in apps {
|
||||
let app_str = &to_string_without_metadata(&app, false).unwrap();
|
||||
archive
|
||||
.write_to_archive(&app_str, &format!("{}.app.json", app.path))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
archive.finish().await?;
|
||||
|
||||
let file = tokio::fs::File::open(file_path).await?;
|
||||
|
||||
@@ -1199,20 +1340,3 @@ async fn tarball_workspace(
|
||||
|
||||
Ok((headers, body))
|
||||
}
|
||||
|
||||
async fn write_to_archive(
|
||||
content: String,
|
||||
path: String,
|
||||
a: &mut tokio_tar::Builder<File>,
|
||||
) -> Result<()> {
|
||||
let bytes = content.as_bytes();
|
||||
let mut header = tokio_tar::Header::new_gnu();
|
||||
header.set_size(bytes.len() as u64);
|
||||
header.set_mtime(0);
|
||||
header.set_uid(0);
|
||||
header.set_gid(0);
|
||||
header.set_mode(0o777);
|
||||
header.set_cksum();
|
||||
a.append_data(&mut header, path, bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ pub struct NewFlow {
|
||||
#[derive(Deserialize, Serialize, Debug, Clone, Default)]
|
||||
pub struct FlowValue {
|
||||
pub modules: Vec<FlowModule>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub failure_module: Option<FlowModule>,
|
||||
#[serde(default)]
|
||||
@@ -152,7 +153,9 @@ pub struct FlowModule {
|
||||
#[serde(alias = "input_transform")]
|
||||
pub input_transforms: HashMap<String, InputTransform>,
|
||||
pub value: FlowModuleValue,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stop_after_if: Option<StopAfterIf>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub summary: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub suspend: Option<Suspend>,
|
||||
@@ -245,7 +248,9 @@ pub enum FlowModuleValue {
|
||||
#[serde(alias = "input_transform")]
|
||||
input_transforms: HashMap<String, InputTransform>,
|
||||
content: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
lock: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<String>,
|
||||
language: ScriptLang,
|
||||
},
|
||||
|
||||
@@ -124,10 +124,8 @@ pub async fn get_latest_hash_for_path<'c>(
|
||||
script_path: &str,
|
||||
) -> error::Result<scripts::ScriptHash> {
|
||||
let script_hash_o = sqlx::query_scalar!(
|
||||
"select hash from script where path = $1 AND (workspace_id = $2 OR workspace_id = \
|
||||
'starter') AND
|
||||
created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND (workspace_id = $2 OR \
|
||||
workspace_id = 'starter')) AND
|
||||
"select hash from script where path = $1 AND workspace_id = $2 AND
|
||||
created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2) AND
|
||||
deleted = false",
|
||||
script_path,
|
||||
w_id
|
||||
|
||||
@@ -351,7 +351,10 @@ pub async fn push<'c>(
|
||||
.unwrap_or(false);
|
||||
|
||||
if !is_super_admin {
|
||||
if usage > MAX_FREE_EXECS {
|
||||
if usage > MAX_FREE_EXECS
|
||||
&& !matches!(job_payload, JobPayload::Dependencies { .. })
|
||||
&& !matches!(job_payload, JobPayload::FlowDependencies { .. })
|
||||
{
|
||||
return Err(error::Error::BadRequest(format!(
|
||||
"User {email} has exceeded the free usage limit of {MAX_FREE_EXECS} that applies outside of premium workspaces."
|
||||
)));
|
||||
@@ -389,8 +392,7 @@ pub async fn push<'c>(
|
||||
match job_payload {
|
||||
JobPayload::ScriptHash { hash, path } => {
|
||||
let language = sqlx::query_scalar!(
|
||||
"SELECT language as \"language: ScriptLang\" FROM script WHERE hash = $1 AND \
|
||||
(workspace_id = $2 OR workspace_id = 'starter')",
|
||||
"SELECT language as \"language: ScriptLang\" FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
hash.0,
|
||||
workspace_id
|
||||
)
|
||||
@@ -441,11 +443,10 @@ pub async fn push<'c>(
|
||||
),
|
||||
JobPayload::FlowDependencies { path } => {
|
||||
let value_json = sqlx::query_scalar!(
|
||||
"SELECT value FROM flow WHERE path = $1 AND (workspace_id = $2 OR workspace_id = \
|
||||
'starter')",
|
||||
path,
|
||||
workspace_id
|
||||
)
|
||||
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
path,
|
||||
workspace_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?
|
||||
.ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", path)))?;
|
||||
@@ -468,11 +469,10 @@ pub async fn push<'c>(
|
||||
}
|
||||
JobPayload::Flow(flow) => {
|
||||
let value_json = sqlx::query_scalar!(
|
||||
"SELECT value FROM flow WHERE path = $1 AND (workspace_id = $2 OR workspace_id = \
|
||||
'starter')",
|
||||
flow,
|
||||
workspace_id
|
||||
)
|
||||
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
flow,
|
||||
workspace_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?
|
||||
.ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", flow)))?;
|
||||
|
||||
@@ -383,7 +383,7 @@ lazy_static::lazy_static! {
|
||||
.unwrap();
|
||||
static ref WORKER_UPTIME_OPTS: prometheus::Opts = prometheus::opts!(
|
||||
"worker_uptime",
|
||||
"Total number of milliseconds since the worker has started"
|
||||
"Total number of seconds since the worker has started"
|
||||
);
|
||||
|
||||
static ref TIMEOUT: u16 = std::env::var("TIMEOUT")
|
||||
@@ -445,15 +445,11 @@ pub async fn run_worker(
|
||||
|
||||
insert_initial_ping(worker_instance, &worker_name, ip, db).await;
|
||||
|
||||
let uptime_metric = prometheus::register_int_counter!(WORKER_UPTIME_OPTS
|
||||
let uptime_metric = prometheus::register_counter!(WORKER_UPTIME_OPTS
|
||||
.clone()
|
||||
.const_label("name", &worker_name))
|
||||
.unwrap();
|
||||
uptime_metric.inc_by(
|
||||
((Instant::now() - start_time).as_millis() - uptime_metric.get() as u128)
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
|
||||
let worker_execution_duration = prometheus::register_histogram_vec!(
|
||||
prometheus::HistogramOpts::new(
|
||||
@@ -465,6 +461,14 @@ pub async fn run_worker(
|
||||
)
|
||||
.expect("register prometheus metric");
|
||||
|
||||
let worker_execution_duration_counter = prometheus::register_counter!(prometheus::opts!(
|
||||
"worker_execution_duration_counter",
|
||||
"Total number of seconds spent executing jobs"
|
||||
)
|
||||
.const_label("name", &worker_name))
|
||||
.expect("register prometheus metric");
|
||||
|
||||
|
||||
let worker_sleep_duration = prometheus::register_histogram!(prometheus::HistogramOpts::new(
|
||||
"worker_sleep_duration",
|
||||
"Duration sleeping waiting for job",
|
||||
@@ -472,6 +476,15 @@ pub async fn run_worker(
|
||||
.const_label("name", &worker_name),)
|
||||
.expect("register prometheus metric");
|
||||
|
||||
|
||||
let worker_sleep_duration_counter = prometheus::register_counter!(prometheus::opts!(
|
||||
"worker_sleep_duration_counter",
|
||||
"Total number of seconds spent sleeping between pulling jobs from the queue"
|
||||
)
|
||||
.const_label("name", &worker_name))
|
||||
.expect("register prometheus metric");
|
||||
|
||||
|
||||
let worker_pull_duration = prometheus::register_histogram!(prometheus::HistogramOpts::new(
|
||||
"worker_pull_duration",
|
||||
"Duration pulling next job",
|
||||
@@ -479,6 +492,13 @@ pub async fn run_worker(
|
||||
.const_label("name", &worker_name),)
|
||||
.expect("register prometheus metric");
|
||||
|
||||
let worker_pull_duration_counter = prometheus::register_counter!(prometheus::opts!(
|
||||
"worker_pull_duration_counter",
|
||||
"Total number of seconds spent pulling jobs (if growing large the db is undersized)"
|
||||
)
|
||||
.const_label("name", &worker_name))
|
||||
.expect("register prometheus metric");
|
||||
|
||||
let worker_execution_failed = prometheus::register_int_counter_vec!(
|
||||
prometheus::Opts::new("worker_execution_failed", "Number of failed jobs",)
|
||||
.const_label("name", &worker_name),
|
||||
@@ -526,11 +546,12 @@ pub async fn run_worker(
|
||||
worker_busy.set(0);
|
||||
|
||||
uptime_metric.inc_by(
|
||||
((Instant::now() - start_time).as_millis() - uptime_metric.get() as u128)
|
||||
(((Instant::now() - start_time).as_millis() as f64)/1000.0 - uptime_metric.get())
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
|
||||
let do_break = async {
|
||||
if last_ping.elapsed().as_secs() > NUM_SECS_ENV_CHECK {
|
||||
sqlx::query!(
|
||||
@@ -574,7 +595,8 @@ pub async fn run_worker(
|
||||
(job, timer) = {
|
||||
let timer = worker_pull_duration.start_timer();
|
||||
pull(&db, WHITELIST_WORKSPACES.clone(), BLACKLIST_WORKSPACES.clone()).map(|x| (x, timer)) } => {
|
||||
drop(timer);
|
||||
let duration_pull_s = timer.stop_and_record();
|
||||
worker_pull_duration_counter.inc_by(duration_pull_s);
|
||||
(false, job)
|
||||
},
|
||||
}
|
||||
@@ -680,6 +702,8 @@ pub async fn run_worker(
|
||||
.await;
|
||||
};
|
||||
|
||||
let duration = _timer.stop_and_record();
|
||||
worker_execution_duration_counter.inc_by(duration);
|
||||
|
||||
if !*KEEP_JOB_DIR && !(is_flow && same_worker) {
|
||||
let _ = tokio::fs::remove_dir_all(job_dir).await;
|
||||
@@ -689,9 +713,9 @@ pub async fn run_worker(
|
||||
|
||||
let _timer = worker_sleep_duration
|
||||
.start_timer();
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE)).await;
|
||||
|
||||
let duration = _timer.stop_and_record();
|
||||
worker_sleep_duration_counter.inc_by(duration);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!(worker = %worker_name, "run_worker: pulling jobs: {}", err);
|
||||
@@ -1051,8 +1075,7 @@ async fn handle_code_execution_job(
|
||||
job.language.to_owned(),
|
||||
),
|
||||
JobKind::Script => sqlx::query_as::<_, (String, Option<String>, Option<ScriptLang>)>(
|
||||
"SELECT content, lock, language FROM script WHERE hash = $1 AND (workspace_id = $2 OR \
|
||||
workspace_id = 'starter')",
|
||||
"SELECT content, lock, language FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
)
|
||||
.bind(&job.script_hash.unwrap_or(ScriptHash(0)).0)
|
||||
.bind(&job.workspace_id)
|
||||
@@ -1774,6 +1797,8 @@ for k, v in list(args.items()):
|
||||
|
||||
try:
|
||||
res = inner_script.main(**args)
|
||||
if type(res).__name__ == 'DataFrame':
|
||||
res = res.values.tolist()
|
||||
res_json = json.dumps(res, separators=(',', ':'), default=str).replace('\n', '')
|
||||
with open("result.json", 'w') as f:
|
||||
f.write(res_json)
|
||||
|
||||
@@ -557,7 +557,7 @@ async fn has_failure_module<'c>(
|
||||
flow: Uuid,
|
||||
tx: &mut sqlx::Transaction<'c, sqlx::Postgres>,
|
||||
) -> Result<bool, Error> {
|
||||
sqlx::query_scalar(
|
||||
sqlx::query_scalar::<_, Option<bool>>(
|
||||
"
|
||||
SELECT raw_flow->'failure_module' != 'null'::jsonb
|
||||
FROM queue
|
||||
@@ -568,6 +568,7 @@ async fn has_failure_module<'c>(
|
||||
.fetch_one(tx)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("error during retrieval of has_failure_module: {e}")))
|
||||
.map(|v| v.unwrap_or(false))
|
||||
}
|
||||
|
||||
fn next_retry(retry: &Retry, status: &RetryStatus) -> Option<(u16, Duration)> {
|
||||
|
||||
@@ -28,12 +28,25 @@ Flow Steps and Logs will be streamed during execution automatically.
|
||||
The CLI can push specifications to a windmill instance. See the
|
||||
[examples/](./examples/) folder for formats.
|
||||
|
||||
### Pushing a folder
|
||||
## Switch to a different workspace
|
||||
|
||||
You can push all files in a folder at once using `wmill push` Files MUST be
|
||||
named resource_name.\<type\>.json. They will be pushed to the remote path they
|
||||
are in, for example the file `u/admin/fib/fib.script.json` will be pushed as a
|
||||
script to u/admin/fib/fib.
|
||||
```
|
||||
wmill workspace switch <workspace_name>
|
||||
```
|
||||
|
||||
## Sync a workspace
|
||||
|
||||
### Pull
|
||||
|
||||
```
|
||||
wmill sync pull
|
||||
```
|
||||
|
||||
### Push
|
||||
|
||||
```
|
||||
wmill sync push
|
||||
```
|
||||
|
||||
### Pushing individual files
|
||||
|
||||
|
||||
105
cli/apps.ts
Normal file
105
cli/apps.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { Any, model, property } from "./decoverto.ts";
|
||||
import {
|
||||
AppService,
|
||||
AppWithLastVersion,
|
||||
colors,
|
||||
microdiff,
|
||||
Policy,
|
||||
} from "./deps.ts";
|
||||
import { Difference, PushDiffs, Resource, setValueByPath } from "./types.ts";
|
||||
|
||||
@model()
|
||||
export class AppFile implements Resource, PushDiffs {
|
||||
@property(Any)
|
||||
value: any;
|
||||
@property(() => String)
|
||||
summary: string;
|
||||
@property(Any)
|
||||
policy: Policy;
|
||||
|
||||
|
||||
constructor(value: string, summary: string, policy: Policy) {
|
||||
this.value = value;
|
||||
this.summary = summary;
|
||||
this.policy = policy;
|
||||
}
|
||||
async pushDiffs(
|
||||
workspace: string,
|
||||
remotePath: string,
|
||||
diffs: Difference[],
|
||||
): Promise<void> {
|
||||
if (await AppService.existsApp({ workspace, path: remotePath })) {
|
||||
console.log(
|
||||
colors.bold.yellow(
|
||||
`Applying ${diffs.length} diffs to existing app...`,
|
||||
),
|
||||
);
|
||||
const changeset: {
|
||||
path?: string | undefined;
|
||||
summary?: string | undefined;
|
||||
value?: any;
|
||||
policy?: Policy | undefined;
|
||||
} = {};
|
||||
for (const diff of diffs) {
|
||||
if (
|
||||
diff.type !== "REMOVE" &&
|
||||
(
|
||||
diff.path[0] !== "value" && diff.path[0] !== "policy" && (
|
||||
diff.path.length !== 1 ||
|
||||
!["path", "summary"].includes(
|
||||
diff.path[0] as string,
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
throw new Error("Invalid app diff with path " + diff.path);
|
||||
}
|
||||
if (diff.type === "CREATE" || diff.type === "CHANGE") {
|
||||
setValueByPath(changeset, diff.path, diff.value);
|
||||
} else if (diff.type === "REMOVE") {
|
||||
setValueByPath(changeset, diff.path, null);
|
||||
}
|
||||
}
|
||||
|
||||
const hasChanges = Object.values(changeset).some((v) =>
|
||||
v !== null && typeof v !== "undefined"
|
||||
);
|
||||
if (!hasChanges) {
|
||||
return;
|
||||
}
|
||||
|
||||
await AppService.updateApp({
|
||||
workspace,
|
||||
path: remotePath,
|
||||
requestBody: changeset,
|
||||
});
|
||||
} else {
|
||||
console.log(colors.yellow.bold("Creating new app..."));
|
||||
await AppService.createApp({
|
||||
workspace,
|
||||
requestBody: {
|
||||
path: remotePath,
|
||||
policy: this.policy,
|
||||
summary: this.summary,
|
||||
value: this.value,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
async push(workspace: string, remotePath: string): Promise<void> {
|
||||
let existing: AppWithLastVersion | undefined;
|
||||
try {
|
||||
existing = await AppService.getAppByPath({
|
||||
workspace: workspace,
|
||||
path: remotePath,
|
||||
});
|
||||
} catch {
|
||||
existing = undefined;
|
||||
}
|
||||
await this.pushDiffs(
|
||||
workspace,
|
||||
remotePath,
|
||||
microdiff(existing ?? {}, this, { cyclesFix: false }),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// windmill
|
||||
export { setClient } from "https://deno.land/x/windmill@v1.61.1/mod.ts";
|
||||
export * from "https://deno.land/x/windmill@v1.61.1/windmill-api/index.ts";
|
||||
export { setClient } from "https://deno.land/x/windmill@v1.69.3/mod.ts";
|
||||
export * from "https://deno.land/x/windmill@v1.69.3/windmill-api/index.ts";
|
||||
|
||||
// cliffy
|
||||
export { Command } from "https://deno.land/x/cliffy@v0.25.7/command/command.ts";
|
||||
@@ -14,9 +14,8 @@ export {
|
||||
DenoLandProvider,
|
||||
UpgradeCommand,
|
||||
} from "https://deno.land/x/cliffy@v0.25.7/command/upgrade/mod.ts";
|
||||
|
||||
export { CompletionsCommand } from "https://deno.land/x/cliffy@v0.25.7/command/completions/mod.ts";
|
||||
// std
|
||||
export { Untar } from "https://deno.land/std@0.176.0/archive/untar.ts";
|
||||
export * as path from "https://deno.land/std@0.176.0/path/mod.ts";
|
||||
export { ensureDir } from "https://deno.land/std@0.176.0/fs/ensure_dir.ts";
|
||||
export {
|
||||
@@ -38,3 +37,5 @@ export {
|
||||
default as microdiff,
|
||||
} from "https://deno.land/x/microdiff@v1.3.1/index.ts";
|
||||
export { default as objectHash } from "https://deno.land/x/object_hash@2.0.3.1/mod.ts";
|
||||
export { default as gitignore_parser } from "npm:gitignore-parser";
|
||||
export { default as JSZip } from "npm:jszip@3.7.1";
|
||||
|
||||
41
cli/flow.ts
41
cli/flow.ts
@@ -10,7 +10,6 @@ import {
|
||||
colors,
|
||||
Command,
|
||||
Flow,
|
||||
FlowModule,
|
||||
FlowService,
|
||||
JobService,
|
||||
microdiff,
|
||||
@@ -19,21 +18,8 @@ import {
|
||||
} from "./deps.ts";
|
||||
import { requireLogin, resolveWorkspace, validatePath } from "./context.ts";
|
||||
import { resolve, track_job } from "./script.ts";
|
||||
import { Any, array, decoverto, model, property } from "./decoverto.ts";
|
||||
import { Any, decoverto, model, property } from "./decoverto.ts";
|
||||
|
||||
@model()
|
||||
export class FlowValueFilePart {
|
||||
@property(array(Any))
|
||||
modules: Array<FlowModule>;
|
||||
@property(Any)
|
||||
failure_module?: FlowModule;
|
||||
@property(() => Boolean)
|
||||
same_worker?: boolean;
|
||||
|
||||
constructor(modules: Array<FlowModule>) {
|
||||
this.modules = modules;
|
||||
}
|
||||
}
|
||||
|
||||
// this is effectively "OpenFlow" but a copy as it is accepted by the CLI
|
||||
@model()
|
||||
@@ -42,13 +28,13 @@ export class FlowFile implements Resource, PushDiffs {
|
||||
summary: string;
|
||||
@property(() => String)
|
||||
description?: string;
|
||||
@property(() => FlowValueFilePart)
|
||||
value: FlowValueFilePart;
|
||||
@property(Any)
|
||||
value: any;
|
||||
@property(Any)
|
||||
schema?: any;
|
||||
|
||||
constructor(summary: string, value: FlowValueFilePart) {
|
||||
this.summary = summary;
|
||||
constructor(value: any, summary?: string) {
|
||||
this.summary = summary ?? "";
|
||||
this.value = value;
|
||||
}
|
||||
async pushDiffs(
|
||||
@@ -64,7 +50,7 @@ export class FlowFile implements Resource, PushDiffs {
|
||||
) {
|
||||
console.log(
|
||||
colors.bold.yellow(
|
||||
`Applying ${diffs.length} diffs to existing flow...`,
|
||||
`Applying ${diffs.length} diffs to existing flow... ${remotePath}`,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -76,6 +62,7 @@ export class FlowFile implements Resource, PushDiffs {
|
||||
description: this.description, // This is OpenAPIed as optional, but isn't
|
||||
schema: this.schema, // Same
|
||||
};
|
||||
const base_changeset = { ...changeset };
|
||||
for (const diff of diffs) {
|
||||
if (
|
||||
diff.type !== "REMOVE" &&
|
||||
@@ -100,14 +87,17 @@ export class FlowFile implements Resource, PushDiffs {
|
||||
v !== null && typeof v !== "undefined"
|
||||
);
|
||||
if (!hasChanges) {
|
||||
console.log(colors.yellow("! Skipping empty changeset"));
|
||||
return;
|
||||
}
|
||||
|
||||
const update = {
|
||||
...changeset,
|
||||
...base_changeset,
|
||||
}
|
||||
await FlowService.updateFlow({
|
||||
workspace: workspace,
|
||||
path: remotePath,
|
||||
requestBody: changeset,
|
||||
requestBody: update,
|
||||
});
|
||||
} else {
|
||||
console.log(colors.bold.yellow("Creating new flow..."));
|
||||
@@ -131,6 +121,7 @@ export class FlowFile implements Resource, PushDiffs {
|
||||
path: remotePath,
|
||||
});
|
||||
} catch {
|
||||
|
||||
remote = undefined;
|
||||
}
|
||||
await this.pushDiffs(
|
||||
@@ -151,7 +142,7 @@ async function push(opts: Options, filePath: string, remotePath: string) {
|
||||
await requireLogin(opts);
|
||||
|
||||
await pushFlow(filePath, workspace.remote, remotePath);
|
||||
console.log(colors.bold.underline.green("Flow successfully pushed"));
|
||||
console.log(colors.bold.underline.green("Flow pushed"));
|
||||
}
|
||||
|
||||
export async function pushFlow(
|
||||
@@ -187,16 +178,14 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
|
||||
}
|
||||
|
||||
new Table()
|
||||
.header(["path", "summary", "edited at", "edited by"])
|
||||
.header(["path", "summary", "edited by"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(
|
||||
total.map((x) => [
|
||||
x.path,
|
||||
x.summary,
|
||||
x.edited_at,
|
||||
x.edited_by,
|
||||
x.description ?? "-",
|
||||
]),
|
||||
)
|
||||
.render();
|
||||
|
||||
@@ -95,7 +95,6 @@ export class FolderFile implements Resource, PushDiffs {
|
||||
v !== null && typeof v !== "undefined"
|
||||
);
|
||||
if (!hasChanges) {
|
||||
console.log(colors.yellow("! Skipping empty changeset"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,12 +104,12 @@ export class FolderFile implements Resource, PushDiffs {
|
||||
requestBody: changeset,
|
||||
});
|
||||
} else {
|
||||
console.log(colors.bold.yellow("Creating new folder..."));
|
||||
console.log(colors.bold.yellow("Creating new folder: " + remotePath));
|
||||
await FolderService.createFolder({
|
||||
workspace: workspace,
|
||||
requestBody: {
|
||||
name: remotePath,
|
||||
extra_perms: this.extra_perms,
|
||||
extra_perms: Object.fromEntries(this.extra_perms?.entries() ?? []),
|
||||
owners: this.owners,
|
||||
},
|
||||
});
|
||||
@@ -134,7 +133,7 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) {
|
||||
console.log(colors.bold.yellow("Pushing resource..."));
|
||||
|
||||
await pushFolder(workspace.workspaceId, filePath, remotePath);
|
||||
console.log(colors.bold.underline.green("Resource successfully pushed"));
|
||||
console.log(colors.bold.underline.green("Resource pushed"));
|
||||
}
|
||||
|
||||
export async function pushFolder(
|
||||
|
||||
12
cli/login.ts
12
cli/login.ts
@@ -1,5 +1,6 @@
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
import { colors, getAvailablePort, Secret, Select } from "./deps.ts";
|
||||
import { open } from 'https://deno.land/x/open/index.ts';
|
||||
|
||||
export async function loginInteractive(remote: string) {
|
||||
let token: string | undefined;
|
||||
@@ -47,13 +48,20 @@ export async function browserLogin(
|
||||
}
|
||||
|
||||
const server = Deno.listen({ transport: "tcp", port });
|
||||
console.log(`Login by going to ${baseUrl}user/cli?port=${port}`);
|
||||
const url = `${baseUrl}user/cli?port=${port}`
|
||||
console.log(`Login by going to ${url}`);
|
||||
try {
|
||||
await open(url)
|
||||
console.log("Opened browser for you");
|
||||
} catch {
|
||||
console.error(`Failed to open browser, please navigate to ${url}`)
|
||||
}
|
||||
const firstConnection = await server.accept();
|
||||
const httpFirstConnection = Deno.serveHttp(firstConnection);
|
||||
const firstRequest = (await httpFirstConnection.nextRequest())!;
|
||||
const params = new URL(firstRequest.request.url!).searchParams;
|
||||
const token = params.get("token");
|
||||
const _workspace = params.get("workspace");
|
||||
// const _workspace = params.get("workspace");
|
||||
await firstRequest?.respondWith(
|
||||
Response.redirect(baseUrl + "user/cli-success", 302),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Command, DenoLandProvider, UpgradeCommand } from "./deps.ts";
|
||||
import { Command, CompletionsCommand, DenoLandProvider, UpgradeCommand } from "./deps.ts";
|
||||
import flow from "./flow.ts";
|
||||
import script from "./script.ts";
|
||||
import workspace from "./workspace.ts";
|
||||
@@ -13,11 +13,12 @@ import sync from "./sync.ts";
|
||||
import { tryResolveVersion } from "./context.ts";
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
|
||||
const VERSION = "v1.64.0";
|
||||
const VERSION = "v1.70.1";
|
||||
|
||||
let command: any = new Command()
|
||||
.name("wmill")
|
||||
.description("A simple CLI tool for windmill.")
|
||||
.action(() => command.showHelp())
|
||||
.globalOption(
|
||||
"--workspace <workspace:string>",
|
||||
"Specify the target workspace. This overrides the default workspace.",
|
||||
@@ -59,7 +60,8 @@ let command: any = new Command()
|
||||
],
|
||||
provider: new DenoLandProvider({ name: "wmill" }),
|
||||
}),
|
||||
);
|
||||
)
|
||||
.command("completions", new CompletionsCommand());
|
||||
|
||||
if (Number.parseInt(VERSION.replace("v", "").replace(".", "")) > 1700) {
|
||||
command = command
|
||||
|
||||
29
cli/pull.ts
29
cli/pull.ts
@@ -1,43 +1,34 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
import { colors, Command, readerFromStreamReader, Untar } from "./deps.ts";
|
||||
import { colors, Command, JSZip } from "./deps.ts";
|
||||
import { Workspace } from "./workspace.ts";
|
||||
|
||||
export async function downloadTar(
|
||||
export async function downloadZip(
|
||||
workspace: Workspace,
|
||||
): Promise<Untar | undefined> {
|
||||
): Promise<JSZip | undefined> {
|
||||
const requestHeaders: HeadersInit = new Headers();
|
||||
requestHeaders.set("Authorization", "Bearer " + workspace.token);
|
||||
requestHeaders.set("Content-Type", "application/octet-stream");
|
||||
|
||||
const tarResponse = await fetch(
|
||||
const zipResponse = await fetch(
|
||||
workspace.remote + "api/w/" + workspace.workspaceId +
|
||||
"/workspaces/tarball",
|
||||
"/workspaces/tarball?archive_type=zip",
|
||||
{
|
||||
headers: requestHeaders,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
|
||||
if (!tarResponse.ok) {
|
||||
if (!zipResponse.ok) {
|
||||
console.log(
|
||||
colors.red(
|
||||
"Failed to request tarball from API " + tarResponse.statusText,
|
||||
"Failed to request tarball from API " + zipResponse.statusText,
|
||||
),
|
||||
);
|
||||
console.log(await tarResponse.text());
|
||||
return undefined;
|
||||
throw new Error(await zipResponse.text());
|
||||
}
|
||||
|
||||
const streamReader = tarResponse.body?.getReader();
|
||||
if (!streamReader) {
|
||||
console.log(colors.red("Failed to read tar request body"));
|
||||
return undefined;
|
||||
}
|
||||
console.log(colors.yellow("Streaming tarball to disk..."));
|
||||
const denoReader = readerFromStreamReader(streamReader);
|
||||
const untar = new Untar(denoReader);
|
||||
return untar;
|
||||
const blob = await zipResponse.blob();
|
||||
return await JSZip.loadAsync(blob);
|
||||
}
|
||||
|
||||
async function stub(
|
||||
|
||||
@@ -60,7 +60,7 @@ export class ResourceTypeFile implements ResourceI, PushDiffs {
|
||||
) {
|
||||
console.log(
|
||||
"Resource type " + remotePath +
|
||||
" is already taken for the current workspace, but cannot be updated. Is this a conflict with starter?",
|
||||
" is already taken for the current workspace, but cannot be updated. Is this a conflict with starter?",
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -91,7 +91,6 @@ export class ResourceTypeFile implements ResourceI, PushDiffs {
|
||||
v !== null && typeof v !== "undefined"
|
||||
);
|
||||
if (!hasChanges) {
|
||||
console.log(colors.yellow("! Skipping empty changeset"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +100,7 @@ export class ResourceTypeFile implements ResourceI, PushDiffs {
|
||||
requestBody: changeset,
|
||||
});
|
||||
} else {
|
||||
console.log(colors.yellow("Creating new resource type..."));
|
||||
console.log(colors.yellow.bold("Creating new resource type..."));
|
||||
await ResourceService.createResourceType({
|
||||
workspace: workspace,
|
||||
requestBody: {
|
||||
@@ -138,7 +137,7 @@ async function push(opts: PushOptions, filePath: string, name: string) {
|
||||
console.log(colors.bold.yellow("Pushing resource..."));
|
||||
|
||||
await pushResourceType(workspace.workspaceId, filePath, name);
|
||||
console.log(colors.bold.underline.green("Resource successfully pushed"));
|
||||
console.log(colors.bold.underline.green("Resource pushed"));
|
||||
}
|
||||
|
||||
async function list(opts: GlobalOptions) {
|
||||
|
||||
@@ -80,7 +80,6 @@ export class ResourceFile implements Resource2, PushDiffs {
|
||||
v !== null && typeof v !== "undefined"
|
||||
);
|
||||
if (!hasChanges) {
|
||||
console.log(colors.yellow("! Skipping empty changeset"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,7 +97,7 @@ export class ResourceFile implements Resource2, PushDiffs {
|
||||
);
|
||||
}
|
||||
|
||||
console.log(colors.yellow("Creating new resource..."));
|
||||
console.log(colors.yellow.bold("Creating new resource..."));
|
||||
await ResourceService.createResource({
|
||||
workspace: workspace,
|
||||
requestBody: {
|
||||
@@ -156,7 +155,7 @@ async function push(opts: PushOptions, filePath: string, remotePath: string) {
|
||||
console.log(colors.bold.yellow("Pushing resource..."));
|
||||
|
||||
await pushResource(workspace.workspaceId, filePath, remotePath);
|
||||
console.log(colors.bold.underline.green("Resource successfully pushed"));
|
||||
console.log(colors.bold.underline.green(`Resource ${remotePath} pushed`));
|
||||
}
|
||||
|
||||
async function list(opts: GlobalOptions) {
|
||||
|
||||
@@ -76,11 +76,81 @@ async function push(
|
||||
|
||||
await requireLogin(opts);
|
||||
await pushScript(filePath, contentPath, workspace.workspaceId, remotePath);
|
||||
console.log(colors.bold.underline.green("Script successfully pushed"));
|
||||
console.log(colors.bold.underline.green(`Script ${remotePath} pushed`));
|
||||
}
|
||||
|
||||
export async function handleScriptMetadata(path: string, workspace: string, alreadySynced: string[]): Promise<boolean> {
|
||||
if (path.endsWith(".script.json")) {
|
||||
const contentPath = await findContentFile(path)
|
||||
return handleFile(contentPath, await Deno.readTextFile(contentPath), workspace, alreadySynced)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleFile(path: string, content: string, workspace: string, alreadySynced: string[]): Promise<boolean> {
|
||||
if (path.endsWith(".ts") || path.endsWith(".py") || path.endsWith(".go") || path.endsWith(".sh")) {
|
||||
if (alreadySynced.includes(path)) {
|
||||
return true
|
||||
}
|
||||
alreadySynced.push(path)
|
||||
const remotePath = path.substring(0, path.length - 3);
|
||||
const metaPath = remotePath + ".script.json";
|
||||
let typed = undefined
|
||||
try {
|
||||
await Deno.stat(metaPath)
|
||||
typed = JSON.parse(await Deno.readTextFile(metaPath))
|
||||
typed = decoverto.type(ScriptFile).plainToInstance(typed);
|
||||
} catch { }
|
||||
const language = inferContentTypeFromFilePath(path);
|
||||
|
||||
try {
|
||||
const remote = await ScriptService.getScriptByPath({
|
||||
workspace,
|
||||
path: remotePath,
|
||||
});
|
||||
await ScriptService.createScript({
|
||||
workspace,
|
||||
requestBody: {
|
||||
content,
|
||||
description: typed.description,
|
||||
language,
|
||||
path: remotePath,
|
||||
summary: typed.summary,
|
||||
is_template: typed.is_template,
|
||||
kind: typed.kind,
|
||||
lock: typed.lock,
|
||||
parent_hash: remote.hash,
|
||||
schema: typed.schema,
|
||||
},
|
||||
});
|
||||
console.log(colors.yellow.bold(`Creating script with a parent ${remotePath}`))
|
||||
} catch {
|
||||
// no parent hash
|
||||
await ScriptService.createScript({
|
||||
workspace: workspace,
|
||||
requestBody: {
|
||||
content,
|
||||
description: typed.description,
|
||||
language,
|
||||
path: remotePath,
|
||||
summary: typed.summary,
|
||||
is_template: typed.is_template,
|
||||
kind: typed.kind,
|
||||
lock: typed.lock,
|
||||
parent_hash: undefined,
|
||||
schema: typed.schema,
|
||||
},
|
||||
});
|
||||
console.log(colors.yellow.bold(`Creating script without parent ${remotePath}`))
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export async function findContentFile(filePath: string) {
|
||||
console.log("Searching " + filePath);
|
||||
const candidates = [
|
||||
filePath.replace(".script.json", ".ts"),
|
||||
filePath.replace(".script.json", ".py"),
|
||||
@@ -104,7 +174,7 @@ export async function findContentFile(filePath: string) {
|
||||
if (validCandidates.length > 1) {
|
||||
throw new Error(
|
||||
"No content path given and more then one candidate found: " +
|
||||
validCandidates.join(", "),
|
||||
validCandidates.join(", "),
|
||||
);
|
||||
}
|
||||
if (validCandidates.length < 1) {
|
||||
@@ -196,16 +266,14 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) {
|
||||
}
|
||||
|
||||
new Table()
|
||||
.header(["path", "hash", "kind", "language", "created at", "created by"])
|
||||
.header(["path", "summary", "language", "created by"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(
|
||||
total.map((x) => [
|
||||
x.path,
|
||||
x.hash,
|
||||
x.kind,
|
||||
x.summary,
|
||||
x.language,
|
||||
x.created_at,
|
||||
x.created_by,
|
||||
]),
|
||||
)
|
||||
|
||||
1306
cli/sync.ts
1306
cli/sync.ts
File diff suppressed because it is too large
Load Diff
23
cli/types.ts
23
cli/types.ts
@@ -6,6 +6,7 @@ import { ScriptFile } from "./script.ts";
|
||||
import { VariableFile } from "./variable.ts";
|
||||
import { path } from "./deps.ts";
|
||||
import { FolderFile } from "./folder.ts";
|
||||
import { AppFile } from "./apps.ts";
|
||||
|
||||
// TODO: Remove this & replace with a "pull" that lets the object either pull the remote version or return undefined.
|
||||
// Then combine those with diffing, which then gives the new push impl
|
||||
@@ -90,7 +91,8 @@ export function inferTypeFromPath(
|
||||
| FlowFile
|
||||
| ResourceFile
|
||||
| ResourceTypeFile
|
||||
| FolderFile {
|
||||
| FolderFile
|
||||
| AppFile {
|
||||
const typeEnding = getTypeStrFromPath(p);
|
||||
|
||||
if (typeEnding === "folder") {
|
||||
@@ -105,6 +107,8 @@ export function inferTypeFromPath(
|
||||
return decoverto.type(ResourceFile).plainToInstance(obj);
|
||||
} else if (typeEnding === "resource-type") {
|
||||
return decoverto.type(ResourceTypeFile).plainToInstance(obj);
|
||||
} else if (typeEnding === "app") {
|
||||
return decoverto.type(AppFile).plainToInstance(obj);
|
||||
} else {
|
||||
throw new Error("infer type unreachable");
|
||||
}
|
||||
@@ -112,12 +116,17 @@ export function inferTypeFromPath(
|
||||
|
||||
export function getTypeStrFromPath(
|
||||
p: string,
|
||||
): "script" | "variable" | "flow" | "resource" | "resource-type" | "folder" {
|
||||
):
|
||||
| "script"
|
||||
| "variable"
|
||||
| "flow"
|
||||
| "resource"
|
||||
| "resource-type"
|
||||
| "folder"
|
||||
| "app" {
|
||||
const parsed = path.parse(p);
|
||||
if (parsed.ext !== ".json") {
|
||||
throw new Error(
|
||||
"Cannot infer type of non-json file " + JSON.stringify(parsed),
|
||||
);
|
||||
if (parsed.ext == ".go" || parsed.ext == ".ts" || parsed.ext == ".sh" || parsed.ext == ".py") {
|
||||
return 'script'
|
||||
}
|
||||
|
||||
if (parsed.name === "folder.meta") {
|
||||
@@ -128,7 +137,7 @@ export function getTypeStrFromPath(
|
||||
if (
|
||||
typeEnding === "script" || typeEnding === "variable" ||
|
||||
typeEnding === "flow" || typeEnding === "resource" ||
|
||||
typeEnding === "resource-type"
|
||||
typeEnding === "resource-type" || typeEnding === "app"
|
||||
) {
|
||||
return typeEnding;
|
||||
} else {
|
||||
|
||||
@@ -94,19 +94,21 @@ export class VariableFile implements Resource, PushDiffs {
|
||||
v !== null && typeof v !== "undefined"
|
||||
);
|
||||
if (!hasChanges) {
|
||||
console.log(colors.yellow("! Skipping empty changeset"));
|
||||
return;
|
||||
}
|
||||
|
||||
await VariableService.updateVariable({
|
||||
workspace,
|
||||
path: remotePath,
|
||||
alreadyEncrypted: true,
|
||||
requestBody: changeset,
|
||||
});
|
||||
|
||||
console.log(changeset);
|
||||
} else {
|
||||
console.log(colors.yellow("Creating new variable..."));
|
||||
console.log(colors.yellow.bold("Creating new variable..."));
|
||||
await VariableService.createVariable({
|
||||
workspace,
|
||||
alreadyEncrypted: true,
|
||||
requestBody: {
|
||||
path: remotePath,
|
||||
description: this.description,
|
||||
@@ -152,7 +154,7 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) {
|
||||
console.log(colors.bold.yellow("Pushing variable..."));
|
||||
|
||||
await pushVariable(workspace.workspaceId, filePath, remotePath);
|
||||
console.log(colors.bold.underline.green("Variable successfully pushed"));
|
||||
console.log(colors.bold.underline.green(`Variable ${remotePath} pushed`));
|
||||
}
|
||||
|
||||
export async function pushVariable(
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"lock": "",
|
||||
"path": null,
|
||||
"type": "rawscript",
|
||||
"content": "// import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main(x: string) {\n console.log(\"Hello from Deno! The argument x is \" + x);\n return x;\n}\n",
|
||||
"content": "// import * as wmill from \"https://deno.land/x/windmill@v1.69.3/mod.ts\"\n\nexport async function main(x: string) {\n console.log(\"Hello from Deno! The argument x is \" + x);\n return x;\n}\n",
|
||||
"language": "deno",
|
||||
"input_transforms": {
|
||||
"x": {
|
||||
|
||||
@@ -151,7 +151,11 @@ async function switchC(opts: GlobalOptions, workspaceName: string) {
|
||||
|
||||
const all = await allWorkspaces();
|
||||
if (all.findIndex((x) => x.name === workspaceName) === -1) {
|
||||
console.log(colors.red.bold("! This workspace name does not exist."));
|
||||
console.log(colors.red.bold(`! This workspace profile ${workspaceName} does not exist locally.`));
|
||||
console.log("available workspace profiles:")
|
||||
for (const w of all) {
|
||||
console.log(' - ' + w.name)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -184,14 +188,8 @@ export async function add(
|
||||
workspaceName = await Input.prompt("Name this workspace:");
|
||||
}
|
||||
|
||||
const all = await allWorkspaces();
|
||||
if (all.findIndex((x) => x.name === workspaceName) !== -1) {
|
||||
console.log(colors.red.bold("! Workspace name already exists"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!workspaceId) {
|
||||
workspaceId = await Input.prompt("Enter the ID of this workspace");
|
||||
workspaceId = await Input.prompt({ message: "Enter the ID of this workspace", default: workspaceName, suggestions: [workspaceName] });
|
||||
}
|
||||
|
||||
if (!remote) {
|
||||
@@ -203,7 +201,13 @@ export async function add(
|
||||
remote = url.toString();
|
||||
} catch {
|
||||
// not a url
|
||||
remote = new URL(await Input.prompt("Enter the Remote URL")).toString();
|
||||
remote = new URL(
|
||||
await Input.prompt({
|
||||
message: "Enter the Remote URL",
|
||||
suggestions: ["https://app.windmill.dev/"],
|
||||
default: "https://app.windmill.dev/"
|
||||
}),
|
||||
).toString();
|
||||
}
|
||||
}
|
||||
remote = new URL(remote).toString(); // add trailing slash in all cases!
|
||||
@@ -213,18 +217,24 @@ export async function add(
|
||||
token = await loginInteractive(remote);
|
||||
}
|
||||
|
||||
setClient(
|
||||
token,
|
||||
remote.endsWith("/") ? remote.substring(0, remote.length - 1) : remote,
|
||||
);
|
||||
let alreadyExists = false
|
||||
try {
|
||||
alreadyExists = await WorkspaceService.existsWorkspace({
|
||||
requestBody: { id: workspaceId },
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(colors.red.bold("! Credentials or instance is invalid. Aborting."));
|
||||
throw e
|
||||
}
|
||||
if (opts.create) {
|
||||
setClient(
|
||||
token,
|
||||
remote.endsWith("/") ? remote.substring(0, remote.length - 1) : remote,
|
||||
);
|
||||
|
||||
if (
|
||||
!await WorkspaceService.existsWorkspace({
|
||||
requestBody: { id: workspaceId },
|
||||
})
|
||||
!alreadyExists
|
||||
) {
|
||||
console.log(colors.yellow("Workspace does not exist. Creating..."));
|
||||
console.log(colors.yellow(`Workspace at id ${workspaceId} on ${remote} does not exist. Creating...`));
|
||||
await WorkspaceService.createWorkspace({
|
||||
requestBody: {
|
||||
id: workspaceId,
|
||||
@@ -233,6 +243,14 @@ export async function add(
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (!alreadyExists) {
|
||||
console.log(colors.red.bold(`! Workspace at id ${workspaceId} on ${remote} does not exist. Re-run with --create to create it. Aborting.`));
|
||||
console.log("On that instance and with those credentials, the workspaces that you can access are:")
|
||||
const workspaces = await WorkspaceService.listWorkspaces()
|
||||
for (const workspace of workspaces) {
|
||||
console.log(`- ${workspace.id} (name: ${workspace.name})`)
|
||||
}
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
await addWorkspace({
|
||||
@@ -240,28 +258,40 @@ export async function add(
|
||||
remote: remote,
|
||||
workspaceId: workspaceId,
|
||||
token: token,
|
||||
});
|
||||
}, opts);
|
||||
await Deno.writeTextFile(
|
||||
(await getRootStore()) + "/activeWorkspace",
|
||||
workspaceName,
|
||||
);
|
||||
console.log(colors.green.underline("Succesfully added workspace!"));
|
||||
console.log(colors.green.underline(`Added workspace ${workspaceName} for ${workspaceId} on ${remote}!`));
|
||||
}
|
||||
|
||||
export async function addWorkspace(workspace: Workspace) {
|
||||
export async function addWorkspace(workspace: Workspace, opts: any) {
|
||||
workspace.remote = new URL(workspace.remote).toString(); // add trailing slash in all cases!
|
||||
const file = await Deno.open((await getRootStore()) + "remotes.ndjson", {
|
||||
append: true,
|
||||
write: true,
|
||||
read: false,
|
||||
read: true,
|
||||
create: true,
|
||||
});
|
||||
await removeWorkspace(workspace.name, true, opts);
|
||||
await file.write(new TextEncoder().encode(JSON.stringify(workspace) + "\n"));
|
||||
file.close();
|
||||
}
|
||||
|
||||
export async function removeWorkspace(name: string) {
|
||||
export async function removeWorkspace(name: string, silent: boolean, opts: any) {
|
||||
const orgWorkspaces = await allWorkspaces();
|
||||
if (orgWorkspaces.findIndex((x) => x.name === name) === -1) {
|
||||
if (!silent) {
|
||||
console.log(colors.red.bold(`! Workspace profile ${name} does not exist locally`));
|
||||
console.log("available workspace profiles:")
|
||||
await list(opts)
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (silent) {
|
||||
console.log(colors.yellow(`Replacing existing workspace ${name}`))
|
||||
}
|
||||
await Deno.writeTextFile(
|
||||
(await getRootStore()) + "remotes.ndjson",
|
||||
orgWorkspaces
|
||||
@@ -269,19 +299,22 @@ export async function removeWorkspace(name: string) {
|
||||
.map((x) => JSON.stringify(x))
|
||||
.join("\n"),
|
||||
);
|
||||
if (!silent) {
|
||||
console.log(colors.green.underline(`Succesfully removed workspace ${name}!`));
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(_opts: GlobalOptions, name: string) {
|
||||
await removeWorkspace(name);
|
||||
console.log(colors.green.underline("Succesfully removed workspace!"));
|
||||
await removeWorkspace(name, false, _opts);
|
||||
}
|
||||
|
||||
const command = new Command()
|
||||
.description("workspace related commands")
|
||||
.action(list as any)
|
||||
.command("switch")
|
||||
.complete("workspace", async () => (await allWorkspaces()).map((x) => x.name))
|
||||
.description("Switch to another workspace")
|
||||
.arguments("<workspace_name:string>")
|
||||
.arguments("<workspace_name:string:workspace>")
|
||||
.action(switchC as any)
|
||||
.command("add")
|
||||
.description("Add a workspace")
|
||||
|
||||
@@ -4,8 +4,9 @@ set -e
|
||||
npm ci --ignore-scripts
|
||||
npx --yes openapi-typescript-codegen --input ../backend/windmill-api/openapi.yaml \
|
||||
--output ./src --useOptions \
|
||||
&& sed -i '213 i \\ request.referrerPolicy = \"no-referrer\"\n' src/core/request.ts
|
||||
npx --yes denoify
|
||||
&& sed -i '213 i \\ request.referrerPolicy = \"no-referrer\"\n' src/core/request.ts
|
||||
npx --yes denoify
|
||||
rm -rf windmill-api
|
||||
mv deno_dist windmill-api
|
||||
sed -i 's/buffer DENOIFY: UNKNOWN NODE BUILTIN/node:buffer/' ./windmill-api/core/request.ts
|
||||
rm -rf src/
|
||||
|
||||
@@ -14,7 +14,6 @@ BigInt.prototype.toJSON = function () {
|
||||
};
|
||||
|
||||
export { pgSql, pgClient } from './pg.ts'
|
||||
export { mySql, mysqlClient } from './mysql.ts'
|
||||
|
||||
export type Sql = string
|
||||
export type Email = string
|
||||
|
||||
258
frontend/package-lock.json
generated
258
frontend/package-lock.json
generated
@@ -1,23 +1,28 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.64.0",
|
||||
"version": "1.70.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill",
|
||||
"version": "1.64.0",
|
||||
"version": "1.70.1",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@leeoniya/ufuzzy": "^0.9.1",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@redocly/json-to-json-schema": "^0.0.1",
|
||||
"@tanstack/svelte-table": "^8.7.6",
|
||||
"ag-grid-svelte": "^0.1.4",
|
||||
"async-mutex": "^0.4.0",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"fast-equals": "^4.0.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
"lucide-svelte": "^0.115.0",
|
||||
"monaco-editor": "0.33.0",
|
||||
"monaco-editor-workers": "0.33.0",
|
||||
"monaco-languageclient": "2.1.0",
|
||||
@@ -25,11 +30,12 @@
|
||||
"svelte-autosize": "^1.0.1",
|
||||
"svelte-chartjs": "^3.1.0",
|
||||
"svelte-portal": "^2.2.0",
|
||||
"svelte-select": "^5.0.2",
|
||||
"svelte-select": "^5.3.1",
|
||||
"tailwind-merge": "^1.9.1",
|
||||
"vscode-ws-jsonrpc": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.29.2",
|
||||
"@playwright/test": "^1.31.1",
|
||||
"@sveltejs/adapter-static": "^1.0.0",
|
||||
"@sveltejs/kit": "^1.0.0-next.589",
|
||||
"@sveltejs/package": "^1.0.2",
|
||||
@@ -39,6 +45,7 @@
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelte-grid": "^5.1.4",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@@ -47,7 +54,6 @@
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"lucide-svelte": "^0.104.0",
|
||||
"openapi-typescript-codegen": "^0.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"postcss": "^8.4.18",
|
||||
@@ -59,7 +65,6 @@
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-awesome": "^3.0.0",
|
||||
"svelte-check": "^3.0.2",
|
||||
"svelte-grid": "^5.1.1",
|
||||
"svelte-highlight": "^6.2.1",
|
||||
"svelte-overlay": "^1.4.1",
|
||||
"svelte-popperjs": "^1.3.2",
|
||||
@@ -599,16 +604,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.5.tgz",
|
||||
"integrity": "sha512-iDdOsaCHZH/0FM0yNBYt+cJxJF9S5jrYWNtDZOiDFMiZ7uxMJ/71h8eTwoVifEAruv9p9rlMPYCGIgMjOz95FQ=="
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz",
|
||||
"integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg=="
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz",
|
||||
"integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.1.tgz",
|
||||
"integrity": "sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.0.5"
|
||||
"@floating-ui/core": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||
@@ -749,19 +754,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz",
|
||||
"integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==",
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
|
||||
"integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.29.2"
|
||||
"playwright-core": "1.31.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
@@ -774,8 +782,6 @@
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
@@ -1261,6 +1267,12 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@windmill-labs/svelte-grid": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.4.tgz",
|
||||
"integrity": "sha512-alxQ8su/osmHodl1JMta+0dvlc2qnYwiXsoeC8Ys8hrLywD03y5tjxYV0L0WXvOnMKG1S9GbqGuDVCk+UmVuKg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@windmill-labs/svelvet": {
|
||||
"version": "4.0.20",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelvet/-/svelvet-4.0.20.tgz",
|
||||
@@ -1350,6 +1362,23 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ag-grid-community": {
|
||||
"version": "28.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-28.2.1.tgz",
|
||||
"integrity": "sha512-DMZh/xD/FqYP17qJ1M92PolTYe+hrKuEaf+A4h13O6qn2x/xZQrTRGW5DgnQLR/uLMe1XXZQPKR3UKgAlKo69A==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ag-grid-svelte": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-svelte/-/ag-grid-svelte-0.1.4.tgz",
|
||||
"integrity": "sha512-yKJKZ1o3aE9dgaK4WtmjG+vA9GKqErznvFisDnzi+PPGWXDCSNWuoDGCUpEFJtHKrfooLoUZZHYHi8I9sZ+BCA==",
|
||||
"dependencies": {
|
||||
"svelte": "^3.55.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ag-grid-community": "^28"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -1864,8 +1893,7 @@
|
||||
"node_modules/chart.js": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
|
||||
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==",
|
||||
"peer": true
|
||||
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w=="
|
||||
},
|
||||
"node_modules/chartjs-adapter-date-fns": {
|
||||
"version": "3.0.0",
|
||||
@@ -2433,7 +2461,8 @@
|
||||
"node_modules/dedent-js": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
|
||||
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="
|
||||
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/deep-extend": {
|
||||
"version": "0.6.0",
|
||||
@@ -2965,6 +2994,11 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz",
|
||||
"integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg=="
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
@@ -3843,6 +3877,7 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
@@ -3859,10 +3894,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-svelte": {
|
||||
"version": "0.104.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.104.0.tgz",
|
||||
"integrity": "sha512-PhckIn28E571Kx2gs2SVh5gc0RlDge521AE63ce4c8gRUMYeo9USPUr80bt/26J5+8qce+hw1zRjx/ag2BqBgw==",
|
||||
"dev": true,
|
||||
"version": "0.115.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.115.0.tgz",
|
||||
"integrity": "sha512-kR7XlwoSVjGX+jwOiHKjTCDMPcTKrsKGyYnJkgW55wyKmWAzfOSESDGuRHcfsRAIQgg133mDNtCm++S8V5rRjg==",
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.49.0"
|
||||
}
|
||||
@@ -4239,6 +4273,7 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
||||
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lower-case": "^2.0.2",
|
||||
"tslib": "^2.0.3"
|
||||
@@ -4473,6 +4508,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
|
||||
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"no-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
@@ -4567,9 +4603,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz",
|
||||
"integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==",
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz",
|
||||
"integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -6177,33 +6213,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-floating-ui": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.1.6.tgz",
|
||||
"integrity": "sha512-lJfrc34+xSxKs+5AtnYj32uRIjaxGjhT92cOiNL3zEqaJ755JxUqXCf6tk+9G1wkCuqkK9A8tscVBsyY3iLmVQ==",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.2.8.tgz",
|
||||
"integrity": "sha512-8Ifi5CD2Ui7FX7NjJRmutFtXjrB8T/FMNoS2H8P81t5LHK4I9G4NIs007rLWG/nRl7y+zJUXa3tWuTjYXw/O5A==",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.4",
|
||||
"svelte2tsx": "^0.5.11"
|
||||
"@floating-ui/core": "^1.1.0",
|
||||
"@floating-ui/dom": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-floating-ui/node_modules/svelte2tsx": {
|
||||
"version": "0.5.23",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.5.23.tgz",
|
||||
"integrity": "sha512-jYFnugTQRFmUpvLXPQrKzVYcW5ErT+0QCxg027Zx9BuvYefMZFuoBSTDYe7viPEFGrPPiLgT2m7f5n9khE7f7Q==",
|
||||
"dependencies": {
|
||||
"dedent-js": "^1.0.1",
|
||||
"pascal-case": "^3.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.24",
|
||||
"typescript": "^4.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-grid": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-grid/-/svelte-grid-5.1.1.tgz",
|
||||
"integrity": "sha512-PJTC4PUejb8RzcUjymWnf1HALqseUBwxl+p+i5uJlDIC1+zXKok1J16IDoHQxXhNj+INcF9KHE7hIm++NurSAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svelte-highlight": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-highlight/-/svelte-highlight-6.2.1.tgz",
|
||||
@@ -6325,12 +6342,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svelte-select": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.0.2.tgz",
|
||||
"integrity": "sha512-SsyIwcRVl38041xk7HoPCAew8D6ypTrMvoNcwOgAEWz9Yycgd/uZ58gMSubxSDtkW/ohuYmmHf5OlGCKIqn9Cw==",
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.3.1.tgz",
|
||||
"integrity": "sha512-TKlH22FEOr/KYNX4dzBJntNzeI0v0eZYU3VEo2uz/gar6xZdhQ3KT81hvD595GfSB7wiFdYc9SO42wrcgm9W4Q==",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.10",
|
||||
"svelte-floating-ui": "1.1.6"
|
||||
"@floating-ui/dom": "^1.2.1",
|
||||
"svelte-floating-ui": "1.2.8"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-splitpanes": {
|
||||
@@ -6468,6 +6485,11 @@
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.9.1.tgz",
|
||||
"integrity": "sha512-ED9MkiUHlmfh58EC1xHRqXcH1IQyRtmDP0AmXlugYkP2tvfu7ejtjFEHJLJt93mQ7ZJkcqSIgm9M394bq5vOJg=="
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.3.tgz",
|
||||
@@ -6663,6 +6685,7 @@
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -7458,16 +7481,16 @@
|
||||
}
|
||||
},
|
||||
"@floating-ui/core": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.5.tgz",
|
||||
"integrity": "sha512-iDdOsaCHZH/0FM0yNBYt+cJxJF9S5jrYWNtDZOiDFMiZ7uxMJ/71h8eTwoVifEAruv9p9rlMPYCGIgMjOz95FQ=="
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz",
|
||||
"integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg=="
|
||||
},
|
||||
"@floating-ui/dom": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz",
|
||||
"integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.1.tgz",
|
||||
"integrity": "sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==",
|
||||
"requires": {
|
||||
"@floating-ui/core": "^1.0.5"
|
||||
"@floating-ui/core": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"@fortawesome/fontawesome-common-types": {
|
||||
@@ -7574,13 +7597,14 @@
|
||||
}
|
||||
},
|
||||
"@playwright/test": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz",
|
||||
"integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==",
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
|
||||
"integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.29.2"
|
||||
"fsevents": "2.3.2",
|
||||
"playwright-core": "1.31.1"
|
||||
}
|
||||
},
|
||||
"@polka/url": {
|
||||
@@ -7592,9 +7616,7 @@
|
||||
"@popperjs/core": {
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
|
||||
},
|
||||
"@redocly/json-to-json-schema": {
|
||||
"version": "0.0.1",
|
||||
@@ -7901,6 +7923,12 @@
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"@windmill-labs/svelte-grid": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelte-grid/-/svelte-grid-5.1.4.tgz",
|
||||
"integrity": "sha512-alxQ8su/osmHodl1JMta+0dvlc2qnYwiXsoeC8Ys8hrLywD03y5tjxYV0L0WXvOnMKG1S9GbqGuDVCk+UmVuKg==",
|
||||
"dev": true
|
||||
},
|
||||
"@windmill-labs/svelvet": {
|
||||
"version": "4.0.20",
|
||||
"resolved": "https://registry.npmjs.org/@windmill-labs/svelvet/-/svelvet-4.0.20.tgz",
|
||||
@@ -7972,6 +8000,20 @@
|
||||
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
|
||||
"dev": true
|
||||
},
|
||||
"ag-grid-community": {
|
||||
"version": "28.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-28.2.1.tgz",
|
||||
"integrity": "sha512-DMZh/xD/FqYP17qJ1M92PolTYe+hrKuEaf+A4h13O6qn2x/xZQrTRGW5DgnQLR/uLMe1XXZQPKR3UKgAlKo69A==",
|
||||
"peer": true
|
||||
},
|
||||
"ag-grid-svelte": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ag-grid-svelte/-/ag-grid-svelte-0.1.4.tgz",
|
||||
"integrity": "sha512-yKJKZ1o3aE9dgaK4WtmjG+vA9GKqErznvFisDnzi+PPGWXDCSNWuoDGCUpEFJtHKrfooLoUZZHYHi8I9sZ+BCA==",
|
||||
"requires": {
|
||||
"svelte": "^3.55.0"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -8316,8 +8358,7 @@
|
||||
"chart.js": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
|
||||
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==",
|
||||
"peer": true
|
||||
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w=="
|
||||
},
|
||||
"chartjs-adapter-date-fns": {
|
||||
"version": "3.0.0",
|
||||
@@ -8736,7 +8777,8 @@
|
||||
"dedent-js": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
|
||||
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="
|
||||
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
@@ -9144,6 +9186,11 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-equals": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz",
|
||||
"integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg=="
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
@@ -9819,6 +9866,7 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
@@ -9832,10 +9880,9 @@
|
||||
}
|
||||
},
|
||||
"lucide-svelte": {
|
||||
"version": "0.104.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.104.0.tgz",
|
||||
"integrity": "sha512-PhckIn28E571Kx2gs2SVh5gc0RlDge521AE63ce4c8gRUMYeo9USPUr80bt/26J5+8qce+hw1zRjx/ag2BqBgw==",
|
||||
"dev": true,
|
||||
"version": "0.115.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.115.0.tgz",
|
||||
"integrity": "sha512-kR7XlwoSVjGX+jwOiHKjTCDMPcTKrsKGyYnJkgW55wyKmWAzfOSESDGuRHcfsRAIQgg133mDNtCm++S8V5rRjg==",
|
||||
"requires": {}
|
||||
},
|
||||
"magic-string": {
|
||||
@@ -10124,6 +10171,7 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
||||
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lower-case": "^2.0.2",
|
||||
"tslib": "^2.0.3"
|
||||
@@ -10295,6 +10343,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
|
||||
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"no-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
@@ -10368,9 +10417,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"playwright-core": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz",
|
||||
"integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==",
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz",
|
||||
"integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
@@ -11490,31 +11539,14 @@
|
||||
}
|
||||
},
|
||||
"svelte-floating-ui": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.1.6.tgz",
|
||||
"integrity": "sha512-lJfrc34+xSxKs+5AtnYj32uRIjaxGjhT92cOiNL3zEqaJ755JxUqXCf6tk+9G1wkCuqkK9A8tscVBsyY3iLmVQ==",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.2.8.tgz",
|
||||
"integrity": "sha512-8Ifi5CD2Ui7FX7NjJRmutFtXjrB8T/FMNoS2H8P81t5LHK4I9G4NIs007rLWG/nRl7y+zJUXa3tWuTjYXw/O5A==",
|
||||
"requires": {
|
||||
"@floating-ui/dom": "^1.0.4",
|
||||
"svelte2tsx": "^0.5.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"svelte2tsx": {
|
||||
"version": "0.5.23",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.5.23.tgz",
|
||||
"integrity": "sha512-jYFnugTQRFmUpvLXPQrKzVYcW5ErT+0QCxg027Zx9BuvYefMZFuoBSTDYe7viPEFGrPPiLgT2m7f5n9khE7f7Q==",
|
||||
"requires": {
|
||||
"dedent-js": "^1.0.1",
|
||||
"pascal-case": "^3.1.1"
|
||||
}
|
||||
}
|
||||
"@floating-ui/core": "^1.1.0",
|
||||
"@floating-ui/dom": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"svelte-grid": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-grid/-/svelte-grid-5.1.1.tgz",
|
||||
"integrity": "sha512-PJTC4PUejb8RzcUjymWnf1HALqseUBwxl+p+i5uJlDIC1+zXKok1J16IDoHQxXhNj+INcF9KHE7hIm++NurSAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"svelte-highlight": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-highlight/-/svelte-highlight-6.2.1.tgz",
|
||||
@@ -11578,12 +11610,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"svelte-select": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.0.2.tgz",
|
||||
"integrity": "sha512-SsyIwcRVl38041xk7HoPCAew8D6ypTrMvoNcwOgAEWz9Yycgd/uZ58gMSubxSDtkW/ohuYmmHf5OlGCKIqn9Cw==",
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.3.1.tgz",
|
||||
"integrity": "sha512-TKlH22FEOr/KYNX4dzBJntNzeI0v0eZYU3VEo2uz/gar6xZdhQ3KT81hvD595GfSB7wiFdYc9SO42wrcgm9W4Q==",
|
||||
"requires": {
|
||||
"@floating-ui/dom": "^1.0.10",
|
||||
"svelte-floating-ui": "1.1.6"
|
||||
"@floating-ui/dom": "^1.2.1",
|
||||
"svelte-floating-ui": "1.2.8"
|
||||
}
|
||||
},
|
||||
"svelte-splitpanes": {
|
||||
@@ -11693,6 +11725,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tailwind-merge": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.9.1.tgz",
|
||||
"integrity": "sha512-ED9MkiUHlmfh58EC1xHRqXcH1IQyRtmDP0AmXlugYkP2tvfu7ejtjFEHJLJt93mQ7ZJkcqSIgm9M394bq5vOJg=="
|
||||
},
|
||||
"tailwindcss": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.3.tgz",
|
||||
@@ -11829,7 +11866,8 @@
|
||||
"typescript": {
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg=="
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.16.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.64.0",
|
||||
"version": "1.70.1",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -15,7 +15,7 @@
|
||||
"test": "playwright test --config=tests-out/playwright.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.29.2",
|
||||
"@playwright/test": "^1.31.1",
|
||||
"@sveltejs/adapter-static": "^1.0.0",
|
||||
"@sveltejs/kit": "^1.0.0-next.589",
|
||||
"@sveltejs/package": "^1.0.2",
|
||||
@@ -25,6 +25,7 @@
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelte-grid": "^5.1.4",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@@ -33,7 +34,6 @@
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"lucide-svelte": "^0.104.0",
|
||||
"openapi-typescript-codegen": "^0.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"postcss": "^8.4.18",
|
||||
@@ -45,7 +45,6 @@
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-awesome": "^3.0.0",
|
||||
"svelte-check": "^3.0.2",
|
||||
"svelte-grid": "^5.1.1",
|
||||
"svelte-highlight": "^6.2.1",
|
||||
"svelte-overlay": "^1.4.1",
|
||||
"svelte-popperjs": "^1.3.2",
|
||||
@@ -64,13 +63,18 @@
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@leeoniya/ufuzzy": "^0.9.1",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@redocly/json-to-json-schema": "^0.0.1",
|
||||
"@tanstack/svelte-table": "^8.7.6",
|
||||
"ag-grid-svelte": "^0.1.4",
|
||||
"async-mutex": "^0.4.0",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"fast-equals": "^4.0.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
"lucide-svelte": "^0.115.0",
|
||||
"monaco-editor": "0.33.0",
|
||||
"monaco-editor-workers": "0.33.0",
|
||||
"monaco-languageclient": "2.1.0",
|
||||
@@ -78,7 +82,8 @@
|
||||
"svelte-autosize": "^1.0.1",
|
||||
"svelte-chartjs": "^3.1.0",
|
||||
"svelte-portal": "^2.2.0",
|
||||
"svelte-select": "^5.0.2",
|
||||
"svelte-select": "^5.3.1",
|
||||
"tailwind-merge": "^1.9.1",
|
||||
"vscode-ws-jsonrpc": "^2.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
11
frontend/src/global.d.ts
vendored
11
frontend/src/global.d.ts
vendored
@@ -9,7 +9,7 @@ declare namespace svelte.JSX {
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'svelte-grid' {
|
||||
declare module '@windmill-labs/svelte-grid' {
|
||||
import type { SvelteComponentTyped } from 'svelte'
|
||||
|
||||
export interface Size {
|
||||
@@ -37,14 +37,14 @@ declare module 'svelte-grid' {
|
||||
|
||||
export interface Props<T> {
|
||||
fillSpace?: boolean
|
||||
items: FilledItem<T>[],
|
||||
items: FilledItem<T>[]
|
||||
rowHeight: number
|
||||
cols: [number, number][]
|
||||
gap?: [number, number]
|
||||
fastStart?: boolean
|
||||
throttleUpdate?: number
|
||||
throttleResize?: number
|
||||
|
||||
onTopId?: string
|
||||
scroller?: undefined
|
||||
sensor?: number
|
||||
}
|
||||
@@ -57,13 +57,14 @@ declare module 'svelte-grid' {
|
||||
Props<T>,
|
||||
{
|
||||
pointerup: CustomEvent<{ id: string }>
|
||||
mount: CustomEvent<>
|
||||
},
|
||||
Slots<T>
|
||||
> { }
|
||||
}
|
||||
|
||||
declare module 'svelte-grid/build/helper/index.mjs' {
|
||||
import { ItemLayout } from 'svelte-grid'
|
||||
declare module '@windmill-labs/svelte-grid/build/helper/index.mjs' {
|
||||
import { ItemLayout } from '@windmill-labs/svelte-grid'
|
||||
|
||||
const x: {
|
||||
normalize(items: any[], col: any): unknown[]
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.svelte-select-list {
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
.nowrap pre code.hljs {
|
||||
whitespace: normal !important;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,13 @@ export type Schema = {
|
||||
|
||||
export type Meta = { ownerKind: OwnerKind; owner: string; name: string }
|
||||
|
||||
type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N
|
||||
? Acc[number]
|
||||
: Enumerate<N, [...Acc, Acc['length']]>
|
||||
|
||||
/** An inclusive range of integer numbers */
|
||||
export type IntRange<F extends number, T extends number> = F | Exclude<Enumerate<T>, Enumerate<F>> | T
|
||||
|
||||
export function pathToMeta(path: string): Meta {
|
||||
const splitted = path.split('/')
|
||||
let ownerKind: OwnerKind
|
||||
|
||||
@@ -396,7 +396,7 @@
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
initialPath=""
|
||||
namePlaceholder="my_{resource_type}"
|
||||
namePlaceholder="{resource_type}"
|
||||
kind="resource"
|
||||
/>
|
||||
|
||||
@@ -417,7 +417,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if resource_type == 'postgresql' || resource_type == 'mysql'}
|
||||
{#if resource_type == 'postgresql' || resource_type == 'mysql' || resource_type == 'mongodb'}
|
||||
<WhitelistIp />
|
||||
{/if}
|
||||
|
||||
@@ -433,7 +433,7 @@
|
||||
{:else}
|
||||
<Path
|
||||
initialPath=""
|
||||
namePlaceholder="my_{resource_type}"
|
||||
namePlaceholder="{resource_type}"
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
kind="resource"
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
import { Highlight } from 'svelte-highlight'
|
||||
import { json } from 'svelte-highlight/languages'
|
||||
import TableCustom from './TableCustom.svelte'
|
||||
import { truncate } from '$lib/utils'
|
||||
import { Button } from './common'
|
||||
import { copyToClipboard, truncate } from '$lib/utils'
|
||||
import { Button, Drawer, DrawerContent } from './common'
|
||||
import autosize from 'svelte-autosize'
|
||||
import { ClipboardCopy } from 'lucide-svelte'
|
||||
|
||||
export let result: any
|
||||
export let requireHtmlApproval = false
|
||||
@@ -86,16 +87,36 @@
|
||||
return 'json'
|
||||
}
|
||||
let payload = ''
|
||||
|
||||
let jsonViewer: Drawer
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={jsonViewer} size="900px">
|
||||
<DrawerContent title="Expanded Result" on:close={jsonViewer.closeDrawer}>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
on:click={() => copyToClipboard(JSON.stringify(result, null, 4))}
|
||||
color="light"
|
||||
size="xs"
|
||||
>
|
||||
<div class="flex gap-2 items-center">Copy to clipboard <ClipboardCopy /> </div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<Highlight language={json} code={JSON.stringify(result, null, 4).replace(/\\n/g, '\n')} />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<div class="inline-highlight">
|
||||
{#if result != undefined}
|
||||
{#if resultKind && resultKind != 'json'}
|
||||
<div class="mb-2 text-gray-500 text-sm bg-gray-50/20">
|
||||
as JSON <input type="checkbox" bind:checked={forceJson} /></div
|
||||
>{/if}{#if typeof result == 'object' && Object.keys(result).length > 0}<div
|
||||
class="mb-2 text-sm text-gray-700"
|
||||
>The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b></div
|
||||
class="mb-2 text-sm text-gray-700 relative"
|
||||
>The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b>
|
||||
<div class="text-gray-500 text-sm absolute top-0 right-2">
|
||||
<button on:click={jsonViewer.openDrawer}>Expand JSON</button>
|
||||
</div></div
|
||||
>{/if}{#if !forceJson && resultKind == 'table-col'}<div
|
||||
class="grid grid-flow-col-dense border border-gray-200 rounded-md "
|
||||
>
|
||||
@@ -223,10 +244,8 @@
|
||||
><a rel="noreferrer" target="_blank" href={result['approvalPage']}>Approval Page</a></div
|
||||
>
|
||||
</div>
|
||||
{:else}<Highlight
|
||||
language={json}
|
||||
code={JSON.stringify(result, null, 4).replace(/\\n/g, '\n')}
|
||||
/>
|
||||
{:else}
|
||||
<Highlight language={json} code={JSON.stringify(result, null, 4).replace(/\\n/g, '\n')} />
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-gray-500 text-sm">No result</div>
|
||||
|
||||
@@ -138,8 +138,18 @@
|
||||
|
||||
export function setCode(ncode: string): void {
|
||||
code = ncode
|
||||
if (editor) {
|
||||
editor.setValue(ncode)
|
||||
if (editor?.getModel()) {
|
||||
// editor.setValue(ncode)
|
||||
editor.pushUndoStop()
|
||||
|
||||
editor.executeEdits('set', [
|
||||
{
|
||||
range: editor.getModel()!.getFullModelRange(), // full range
|
||||
text: ncode
|
||||
}
|
||||
])
|
||||
|
||||
editor.pushUndoStop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
|
||||
import { OFFSET } from './CronInput.svelte'
|
||||
import FlowGraphViewer from './FlowGraphViewer.svelte'
|
||||
import ScriptEditorDrawer from './flows/content/ScriptEditorDrawer.svelte'
|
||||
import FlowEditor from './flows/FlowEditor.svelte'
|
||||
import { flowStateStore } from './flows/flowState'
|
||||
import { flowStore } from './flows/flowStore'
|
||||
@@ -145,6 +146,7 @@
|
||||
|
||||
const scheduleStore = writable<Schedule>({ args: {}, cron: '', enabled: false })
|
||||
const previewArgsStore = writable<Record<string, any>>(initialArgs)
|
||||
const scriptEditorDrawer = writable<ScriptEditorDrawer | undefined>(undefined)
|
||||
|
||||
function select(selectedId: string) {
|
||||
selectedIdStore.set(selectedId)
|
||||
@@ -154,7 +156,8 @@
|
||||
selectedId: selectedIdStore,
|
||||
schedule: scheduleStore,
|
||||
select,
|
||||
previewArgs: previewArgsStore
|
||||
previewArgs: previewArgsStore,
|
||||
scriptEditorDrawer
|
||||
})
|
||||
|
||||
async function loadSchedule() {
|
||||
@@ -181,6 +184,7 @@
|
||||
</script>
|
||||
|
||||
{#if !$userStore?.operator}
|
||||
<ScriptEditorDrawer bind:this={$scriptEditorDrawer} />
|
||||
<UnsavedConfirmationModal />
|
||||
|
||||
<Drawer bind:this={flowViewer} size="75%">
|
||||
|
||||
@@ -106,7 +106,8 @@
|
||||
<div>
|
||||
<Icon class="text-gray-700" data={faUser} scale={SMALL_ICON_SCALE} /><span class="mx-2">
|
||||
By {job.created_by}
|
||||
{#if job.permissioned_as !== `u/${job.created_by}`}but permissioned as {job.permissioned_as}{/if}
|
||||
{#if job.permissioned_as !== `u/${job.created_by}` && job.permissioned_as != job.created_by}but
|
||||
permissioned as {job.permissioned_as}{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
</div>
|
||||
<FlowProgressBar {job} bind:reset={jobProgressReset} />
|
||||
|
||||
<div class="overflow-y-auto grow divide-y divide-gray-600 ">
|
||||
<div class="overflow-y-auto grow divide-y divide-gray-600 pr-4">
|
||||
<div class="max-h-1/2 overflow-auto border-b border-gray-700">
|
||||
<SchemaForm
|
||||
noVariablePicker
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
localFlowModuleStates?.[innerModules?.[i - 1]?.id ?? '']?.type ==
|
||||
FlowStatusModule.type.SUCCESS
|
||||
) {
|
||||
localFlowModuleStates[mod.id ?? ''] = { type: mod.type }
|
||||
localFlowModuleStates[mod.id ?? ''] = { type: mod.type, args: job?.args }
|
||||
} else if (
|
||||
mod.type === FlowStatusModule.type.WAITING_FOR_EXECUTOR &&
|
||||
localFlowModuleStates[mod.id ?? '']?.scheduled_for == undefined
|
||||
@@ -109,7 +109,8 @@
|
||||
type: mod.type,
|
||||
scheduled_for: 'scheduled for ' + displayDate(job?.['scheduled_for'], true),
|
||||
job_id: job?.id,
|
||||
parent_module: mod['parent_module']
|
||||
parent_module: mod['parent_module'],
|
||||
args: job?.args
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -329,10 +330,12 @@
|
||||
type: FlowStatusModule.type.IN_PROGRESS,
|
||||
logs: e.detail.logs,
|
||||
job_id: e.detail.id,
|
||||
args: e.detail.args,
|
||||
iteration_total: flowJobIds?.flowJobs.length
|
||||
}
|
||||
} else {
|
||||
localFlowModuleStates[flowJobIds.moduleId] = {
|
||||
args: e.detail.args,
|
||||
type: e.detail.success
|
||||
? FlowStatusModule.type.SUCCESS
|
||||
: FlowStatusModule.type.FAILURE,
|
||||
@@ -397,10 +400,12 @@
|
||||
localFlowModuleStates[mod.id] = {
|
||||
type: FlowStatusModule.type.IN_PROGRESS,
|
||||
logs: e.detail.logs,
|
||||
args: e.detail.args,
|
||||
parent_module: mod['parent_module']
|
||||
}
|
||||
} else {
|
||||
localFlowModuleStates[mod.id] = {
|
||||
args: e.detail.args,
|
||||
type: e.detail.success
|
||||
? FlowStatusModule.type.SUCCESS
|
||||
: FlowStatusModule.type.FAILURE,
|
||||
@@ -497,6 +502,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="px-1 border-b border-black">
|
||||
<JobArgs args={node.args} />
|
||||
</div>
|
||||
<FlowJobResult
|
||||
loading={job['running'] == true}
|
||||
noBorder
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
can_write =
|
||||
$userStore != undefined &&
|
||||
(folder?.owners.includes('u/' + $userStore.username) ||
|
||||
($userStore.is_admin ?? false) ||
|
||||
($userStore.is_admin ?? false) || ($userStore.is_super_admin ?? false) ||
|
||||
$userStore.pgroups.findIndex((x) => folder?.owners.includes(x)) != -1)
|
||||
|
||||
perms = Array.from(
|
||||
@@ -168,7 +168,8 @@
|
||||
path: name,
|
||||
kind: 'folder',
|
||||
requestBody: {
|
||||
owner: owner_name
|
||||
owner: owner_name,
|
||||
write: true
|
||||
}
|
||||
})
|
||||
} else if (role == 'writer') {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import FolderEditor from './FolderEditor.svelte'
|
||||
import GroupEditor from './GroupEditor.svelte'
|
||||
import { random_adj } from './random_positive_adjetive'
|
||||
|
||||
type PathKind = 'resource' | 'script' | 'variable' | 'flow' | 'schedule' | 'app'
|
||||
let meta: Meta | undefined = undefined
|
||||
@@ -69,15 +70,23 @@
|
||||
|
||||
export async function reset() {
|
||||
if (path == '' || path == 'u//') {
|
||||
meta = { ownerKind: 'user', name: namePlaceholder, owner: '' }
|
||||
meta = {
|
||||
ownerKind: 'user',
|
||||
name: random_adj() + '_' + namePlaceholder,
|
||||
owner: ''
|
||||
}
|
||||
|
||||
meta.owner = $userStore!.username.split('@')[0]
|
||||
|
||||
let i = 1
|
||||
while (await pathExists(metaToPath(meta), kind)) {
|
||||
meta.name = `${namePlaceholder}_${i}`
|
||||
i += 1
|
||||
let newMeta = { ...meta }
|
||||
while (await pathExists(metaToPath(newMeta), kind)) {
|
||||
disabled = true
|
||||
error = 'finding an available name...'
|
||||
newMeta.name = random_adj() + '_' + namePlaceholder
|
||||
}
|
||||
error = ''
|
||||
disabled = false
|
||||
meta = newMeta
|
||||
path = metaToPath(meta)
|
||||
} else {
|
||||
meta = pathToMeta(path)
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
disabled={!can_write}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder="my_resource"
|
||||
namePlaceholder="resource"
|
||||
kind="resource"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import Select from 'svelte-select'
|
||||
import AppConnect from './AppConnect.svelte'
|
||||
import ResourceEditor from './ResourceEditor.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '../defaults'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let resources: Resource[] = []
|
||||
@@ -59,12 +60,13 @@
|
||||
<div class="flex flex-row gap-x-1 w-full">
|
||||
<Select
|
||||
listAutoWidth={false}
|
||||
--height="34px"
|
||||
value={collection.find((x) => x.value == value)}
|
||||
bind:justValue={value}
|
||||
items={collection}
|
||||
class="text-clip grow min-w-0"
|
||||
placeholder="{resourceType} resource"
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles}
|
||||
/>
|
||||
{#if value && value != ''}
|
||||
<Button variant="border" size="xs" on:click={() => resourceEditor?.initEdit?.(value ?? '')}>
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder={'my_schedule'}
|
||||
namePlaceholder="schedule"
|
||||
kind="schedule"
|
||||
/>
|
||||
<div class="mb-8" />
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import { Button, Kbd } from './common'
|
||||
import { faChevronDown, faChevronUp, faPen, faSave } from '@fortawesome/free-solid-svg-icons'
|
||||
import Breadcrumb from './common/breadcrumb/Breadcrumb.svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import LanguageIcon from './common/languageIcons/LanguageIcon.svelte'
|
||||
import type { SupportedLanguage } from '$lib/common'
|
||||
|
||||
@@ -25,6 +24,7 @@
|
||||
export let initialPath: string = ''
|
||||
export let template: 'pgsql' | 'mysql' | 'script' = 'script'
|
||||
export let initialArgs: Record<string, any> = {}
|
||||
export let lockedLanguage = false
|
||||
|
||||
const langs: [string, SupportedLanguage][] = [
|
||||
['Typescript', Script.language.DENO],
|
||||
@@ -84,7 +84,7 @@
|
||||
}
|
||||
})
|
||||
sendUserToast(`New script created at hash ${newHash}`)
|
||||
await goto(`/scripts/edit/${newHash}?step=2`)
|
||||
history.replaceState(history.state, '', `/scripts/edit/${newHash}?step=2`)
|
||||
goto(`/scripts/get/${newHash}?workspace_id=${$workspaceStore}`)
|
||||
} catch (error) {
|
||||
sendUserToast(`Impossible to save the script: ${error.body}`, true)
|
||||
@@ -205,7 +205,7 @@
|
||||
bind:path={script.path}
|
||||
{initialPath}
|
||||
on:enter={() => changeStep(2)}
|
||||
namePlaceholder="my_script"
|
||||
namePlaceholder="script"
|
||||
kind="script"
|
||||
/>
|
||||
<h2 class="border-b pb-1 mt-12 mb-4">Summary</h2>
|
||||
@@ -215,7 +215,13 @@
|
||||
bind:value={script.summary}
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
/>
|
||||
|
||||
<h2 class="border-b pb-1 mt-12 mb-6">Language</h2>
|
||||
{#if lockedLanguage}
|
||||
<div class="text-sm text-gray-600 italic mb-2">
|
||||
As a forked script, the language '{script.language}' cannot be modified.
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-row gap-2 flex-wrap">
|
||||
{#each langs as [label, lang]}
|
||||
{@const isPicked = script.language == lang && template == 'script'}
|
||||
@@ -229,6 +235,7 @@
|
||||
template = 'script'
|
||||
initContent(lang, script.kind, template)
|
||||
}}
|
||||
disabled={lockedLanguage}
|
||||
>
|
||||
<LanguageIcon {lang} /><span class="ml-2 py-4">{label}</span>
|
||||
</Button>
|
||||
@@ -238,6 +245,7 @@
|
||||
variant="border"
|
||||
color={template == 'pgsql' ? 'blue' : 'dark'}
|
||||
btnClasses={template == 'pgsql' ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
|
||||
disabled={lockedLanguage}
|
||||
on:click={() => {
|
||||
script.language = Script.language.DENO
|
||||
template = 'pgsql'
|
||||
@@ -246,7 +254,7 @@
|
||||
>
|
||||
<LanguageIcon lang="pgsql" /><span class="ml-2 py-4">PostgreSQL</span>
|
||||
</Button>
|
||||
<Button
|
||||
<!-- <Button
|
||||
size="sm"
|
||||
variant="border"
|
||||
color={template == 'mysql' ? 'blue' : 'dark'}
|
||||
@@ -258,7 +266,7 @@
|
||||
}}
|
||||
>
|
||||
<LanguageIcon lang="mysql" /><span class="ml-2 py-4">MySQL</span>
|
||||
</Button>
|
||||
</Button> -->
|
||||
</div>
|
||||
<div class="mt-16 mb-4">
|
||||
<Button
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
|
||||
onMount(() => {
|
||||
inferSchema(code)
|
||||
loadPastTests()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import { Button, Drawer, DrawerContent } from './common'
|
||||
import HighlightCode from './HighlightCode.svelte'
|
||||
import FlowPathViewer from './flows/content/FlowPathViewer.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '../defaults'
|
||||
|
||||
export let initialPath: string | undefined = undefined
|
||||
export let scriptPath: string | undefined = undefined
|
||||
@@ -87,6 +88,8 @@
|
||||
bind:justValue={scriptPath}
|
||||
{items}
|
||||
placeholder="Pick a {itemKind}"
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -110,6 +110,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
export function focus() {
|
||||
editor?.focus()
|
||||
}
|
||||
|
||||
let width = 0
|
||||
async function loadMonaco() {
|
||||
model = meditor.createModel(code, lang, mUri.parse(uri))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let options: {
|
||||
left?: string
|
||||
@@ -7,6 +8,8 @@
|
||||
} = {}
|
||||
export let checked: boolean = false
|
||||
export let disabled = false
|
||||
export let textClass = ''
|
||||
export let textStyle = ''
|
||||
|
||||
export let size: 'sm' | 'xs' = 'sm'
|
||||
const id = (Math.random() + 1).toString(36).substring(10)
|
||||
@@ -22,7 +25,13 @@
|
||||
>
|
||||
{#if Boolean(options?.left)}
|
||||
<span
|
||||
class="mr-2 text-sm font-medium duration-200 {disabled ? 'text-gray-600' : 'text-gray-900'}"
|
||||
class={twMerge(
|
||||
'ml-2 font-medium duration-200',
|
||||
disabled ? 'text-gray-500' : 'text-gray-900',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
style={textStyle}
|
||||
>
|
||||
{options?.left}
|
||||
</span>
|
||||
@@ -51,9 +60,13 @@
|
||||
</div>
|
||||
{#if Boolean(options?.right)}
|
||||
<span
|
||||
class="ml-2 text-sm font-medium duration-200
|
||||
{disabled ? 'text-gray-500' : 'text-gray-900'}
|
||||
{size === 'xs' ? 'text-xs' : 'text-sm'}"
|
||||
class={twMerge(
|
||||
'ml-2 font-medium duration-200',
|
||||
disabled ? 'text-gray-500' : 'text-gray-900',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
style={textStyle}
|
||||
>
|
||||
{options?.right}
|
||||
</span>
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder="my_variable"
|
||||
namePlaceholder="variable"
|
||||
kind="variable"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
.filter((worker) => {
|
||||
const date = new Date().getTime() - 300 * 60
|
||||
const ping_at = new Date(worker.ping_at).getTime()
|
||||
return ping_at > date
|
||||
return worker.ip != 'unretrievable IP' && ping_at > date
|
||||
})
|
||||
.map((worker) => worker.ip)
|
||||
)
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<script lang="ts">
|
||||
import DisplayResult from '$lib/components/DisplayResult.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../inputType'
|
||||
import { IS_APP_PUBLIC_CONTEXT_KEY } from '../types'
|
||||
import RunnableWrapper from './helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let initializing: boolean | undefined = undefined
|
||||
|
||||
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
|
||||
let result: any = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
</script>
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<div class="w-full border-b px-2 text-xs p-1 font-semibold bg-gray-500 text-white rounded-t-sm">
|
||||
Results
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<DisplayResult {result} {requireHtmlApproval} />
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
@@ -1,14 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { Button, type ButtonType } from '$lib/components/common'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import { loadIcon } from '../icon'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
@@ -19,10 +20,11 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let noWFull = false
|
||||
export let preclickAction: (() => Promise<void>) | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'button'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { runnableComponents, worldStore, app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let labelValue: string
|
||||
let color: ButtonType.Color
|
||||
@@ -34,12 +36,36 @@
|
||||
|
||||
let isLoading: boolean = false
|
||||
let ownClick: boolean = false
|
||||
let triggerOnAppLoad = false
|
||||
|
||||
let beforeIcon: undefined | string = undefined
|
||||
let afterIcon: undefined | string = undefined
|
||||
|
||||
let beforeIconComponent: any
|
||||
let afterIconComponent: any
|
||||
|
||||
$: beforeIcon && handleBeforeIcon()
|
||||
$: afterIcon && handleAfterIcon()
|
||||
|
||||
async function handleBeforeIcon() {
|
||||
if (beforeIcon) {
|
||||
beforeIconComponent = await loadIcon(beforeIcon)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAfterIcon() {
|
||||
if (afterIcon) {
|
||||
afterIconComponent = await loadIcon(afterIcon)
|
||||
}
|
||||
}
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
result: Output<Array<any>>
|
||||
loading: Output<boolean>
|
||||
}
|
||||
|
||||
$: triggerOnAppLoad && runnableComponent?.runComponent()
|
||||
|
||||
$: if (outputs?.loading != undefined) {
|
||||
outputs.loading.set(false, true)
|
||||
}
|
||||
@@ -64,8 +90,11 @@
|
||||
<InputValue {id} input={configuration.goto} bind:value={goto} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.beforeIcon} bind:value={beforeIcon} />
|
||||
<InputValue {id} input={configuration.afterIcon} bind:value={afterIcon} />
|
||||
<InputValue {id} input={configuration.triggerOnAppLoad} bind:value={triggerOnAppLoad} />
|
||||
|
||||
<InputValue
|
||||
row={extraQueryParams['row']}
|
||||
{id}
|
||||
input={configuration.disabled}
|
||||
bind:value={disabled}
|
||||
@@ -87,7 +116,12 @@
|
||||
<div class="text-red-500 text-xs">{errorsMessage}</div>
|
||||
{/if}
|
||||
<Button
|
||||
btnClasses={fillContainer ? 'w-full h-full' : ''}
|
||||
btnClasses={twMerge(
|
||||
$app.css?.['buttoncomponent']?.['button']?.class,
|
||||
customCss?.button.class,
|
||||
fillContainer ? 'w-full h-full' : ''
|
||||
)}
|
||||
style={[$app.css?.['buttoncomponent']?.['button']?.style, customCss?.button.style].join(';')}
|
||||
{disabled}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
@@ -112,8 +146,14 @@
|
||||
{color}
|
||||
{loading}
|
||||
>
|
||||
<span class="truncate">
|
||||
{labelValue}
|
||||
<span class="truncate inline-flex gap-2 items-center">
|
||||
{#if beforeIconComponent}
|
||||
<svelte:component this={beforeIconComponent} size={14} />
|
||||
{/if}
|
||||
<div>{labelValue}</div>
|
||||
{#if afterIconComponent}
|
||||
<svelte:component this={afterIconComponent} size={14} />
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
@@ -17,10 +18,11 @@
|
||||
export let recomputeIds: string[] | undefined = undefined
|
||||
export let extraQueryParams: Record<string, any> = {}
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container' | 'button'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let labelValue: string = 'Default label'
|
||||
let color: ButtonType.Color
|
||||
@@ -47,6 +49,8 @@
|
||||
isLoading = value
|
||||
}
|
||||
})
|
||||
|
||||
$: css = concatCustomCss($app.css?.formcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.goto} bind:value={goto} />
|
||||
@@ -64,9 +68,13 @@
|
||||
autoRefresh={false}
|
||||
forceSchemaDisplay={true}
|
||||
runnableClass="!block"
|
||||
runnableStyle={css?.container.style}
|
||||
>
|
||||
<AlignWrapper {horizontalAlignment}>
|
||||
<div class="flex flex-col gap-2 px-4 w-full">
|
||||
<div
|
||||
class="flex flex-col gap-2 px-4 w-full {css?.container?.class ?? ''}"
|
||||
style={css?.container?.style ?? ''}
|
||||
>
|
||||
<div>
|
||||
{#if noInputs}
|
||||
<div class="text-gray-600 italic text-sm my-4">
|
||||
@@ -84,7 +92,8 @@
|
||||
{#if !noInputs}
|
||||
<Button
|
||||
loading={isLoading}
|
||||
btnClasses="my-1"
|
||||
btnClasses="my-1 {css?.button?.class ?? ''}"
|
||||
style={css?.button?.style ?? ''}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
@@ -5,13 +5,14 @@
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import Portal from 'svelte-portal'
|
||||
import Modal from '$lib/components/common/modal/Modal.svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
@@ -20,10 +21,11 @@
|
||||
export let extraQueryParams: Record<string, any> = {}
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'button' | 'popup'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app, runnableComponents, worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let labelValue: string = 'Default label'
|
||||
let color: ButtonType.Color
|
||||
@@ -35,6 +37,8 @@
|
||||
let ownClick: boolean = false
|
||||
|
||||
let errors: Record<string, string> = {}
|
||||
let open: boolean = false
|
||||
|
||||
$: errorsMessage = Object.values(errors)
|
||||
.filter((x) => x != '')
|
||||
.join('\n')
|
||||
@@ -62,14 +66,13 @@
|
||||
|
||||
$: loading = isLoading && ownClick
|
||||
|
||||
let open: boolean = false
|
||||
$: css = concatCustomCss($app?.css?.formbuttoncomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue
|
||||
row={extraQueryParams['row']}
|
||||
{id}
|
||||
input={configuration.disabled}
|
||||
bind:value={disabled}
|
||||
@@ -80,6 +83,8 @@
|
||||
<Modal
|
||||
{open}
|
||||
title={labelValue}
|
||||
class={css?.popup.class}
|
||||
style={css?.popup.style}
|
||||
on:canceled={() => {
|
||||
open = false
|
||||
}}
|
||||
@@ -149,6 +154,8 @@
|
||||
{disabled}
|
||||
{size}
|
||||
{color}
|
||||
btnClasses={css?.button?.class ?? ''}
|
||||
style={css?.button?.style ?? ''}
|
||||
on:click={(e) => {
|
||||
open = true
|
||||
}}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as AppButton } from './AppButton.svelte'
|
||||
export { default as AppForm } from './AppForm.svelte'
|
||||
export { default as AppFormButton } from './AppFormButton.svelte'
|
||||
@@ -11,16 +11,22 @@
|
||||
CategoryScale,
|
||||
BarElement
|
||||
} from 'chart.js'
|
||||
import type { ChartOptions } from 'chart.js'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
@@ -45,7 +51,18 @@
|
||||
theme3: ['#e74a3b', '#4e73df', '#1cc88a', '#36b9cc', '#f6c23e']
|
||||
}[theme]
|
||||
|
||||
const options = {
|
||||
const lineOptions: ChartOptions<'line'> = {
|
||||
responsive: true,
|
||||
animation: false,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const barOptions: ChartOptions<'bar'> = {
|
||||
responsive: true,
|
||||
animation: false,
|
||||
maintainAspectRatio: false,
|
||||
@@ -65,17 +82,21 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.barchartcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.theme} bind:value={theme} />
|
||||
<InputValue {id} input={configuration.line} bind:value={lineChart} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if result}
|
||||
{#if lineChart}
|
||||
<Line {data} {options} />
|
||||
{:else}
|
||||
<Bar {data} {options} />
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
{#if lineChart}
|
||||
<Line {data} options={lineOptions} />
|
||||
{:else}
|
||||
<Bar {data} options={barOptions} />
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import DisplayResult from '$lib/components/DisplayResult.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import { IS_APP_PUBLIC_CONTEXT_KEY, type AppEditorContext, type ComponentCustomCSS } from '../../types'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'header' | 'container'> | undefined = undefined
|
||||
|
||||
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let result: any = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
</script>
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
<div class={twMerge(
|
||||
'w-full border-b px-2 text-xs p-1 font-semibold bg-gray-500 text-white rounded-t-sm',
|
||||
$app.css?.['displaycomponent']?.['header']?.class,
|
||||
customCss?.header?.class
|
||||
)}>
|
||||
Results
|
||||
</div>
|
||||
<div class={twMerge(
|
||||
'p-2',
|
||||
$app.css?.['displaycomponent']?.['container']?.class,
|
||||
customCss?.container?.class
|
||||
)}>
|
||||
<DisplayResult {result} {requireHtmlApproval} />
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
@@ -1,16 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let result: string | undefined = undefined
|
||||
let h: number | undefined = undefined
|
||||
let w: number | undefined = undefined
|
||||
|
||||
$: css = concatCustomCss($app.css?.htmlcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -25,8 +32,8 @@
|
||||
{#key result}
|
||||
<iframe
|
||||
frameborder="0"
|
||||
style="height: {h}px; width: {w}px"
|
||||
class="p-0"
|
||||
style="height: {h}px; width: {w}px; {css?.container?.style ?? ''}"
|
||||
class="p-0 {css?.container?.class ?? ''}"
|
||||
title="sandbox"
|
||||
srcdoc={result
|
||||
? '<scr' + `ipt type="application/javascript" src="/tailwind.js"></script>` + result
|
||||
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import type { AppInput } from '../../inputType'
|
||||
import { AlignWrapper, InputValue, RunnableWrapper } from '../helpers'
|
||||
import { loadIcon } from '../icon'
|
||||
|
||||
export let id: string
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = 'left'
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = []
|
||||
|
||||
let icon: string | undefined = undefined
|
||||
let size: number
|
||||
let color: string
|
||||
let strokeWidth: number
|
||||
let iconComponent: any
|
||||
|
||||
$: icon && handleIcon()
|
||||
|
||||
async function handleIcon() {
|
||||
if (icon) {
|
||||
iconComponent = await loadIcon(icon)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.icon} bind:value={icon} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.strokeWidth} bind:value={strokeWidth} />
|
||||
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
{#if iconComponent}
|
||||
<svelte:component
|
||||
this={iconComponent}
|
||||
size={size || 24}
|
||||
color={color || 'currentColor'}
|
||||
strokeWidth={strokeWidth || 2}
|
||||
/>
|
||||
{/if}
|
||||
</AlignWrapper>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import type { staticValues } from '../../editor/componentsPanel/componentStaticValues'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
type FitOption = (typeof staticValues)['objectFitOptions'][number]
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export const staticOutputs: string[] = ['loading']
|
||||
|
||||
const fit: Record<FitOption, string> = {
|
||||
cover: 'object-cover',
|
||||
contain: 'object-contain',
|
||||
fill: 'object-fill'
|
||||
}
|
||||
|
||||
let source: string | undefined = undefined
|
||||
let imageFit: FitOption | undefined = undefined
|
||||
let altText: string | undefined = undefined
|
||||
let customStyles: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.source} bind:value={source} />
|
||||
<InputValue {id} input={configuration.imageFit} bind:value={imageFit} />
|
||||
<InputValue {id} input={configuration.altText} bind:value={altText} />
|
||||
<InputValue {id} input={configuration.customStyles} bind:value={customStyles} />
|
||||
|
||||
<img
|
||||
on:pointerdown|preventDefault
|
||||
src={source}
|
||||
alt={altText}
|
||||
style={customStyles}
|
||||
class="w-full h-full {fit[imageFit || 'cover']}"
|
||||
/>
|
||||
@@ -14,13 +14,18 @@
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
@@ -60,17 +65,21 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.piechartcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.theme} bind:value={theme} />
|
||||
<InputValue {id} input={configuration.doughnutStyle} bind:value={doughnut} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if result}
|
||||
{#if doughnut}
|
||||
<Doughnut {data} {options} />
|
||||
{:else}
|
||||
<Pie {data} {options} />
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
{#if doughnut}
|
||||
<Doughnut {data} {options} />
|
||||
{:else}
|
||||
<Pie {data} {options} />
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
@@ -9,22 +9,29 @@
|
||||
LinearScale,
|
||||
PointElement,
|
||||
CategoryScale,
|
||||
BarElement
|
||||
BarElement,
|
||||
type Point
|
||||
} from 'chart.js'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import Scatter from 'svelte-chartjs/Scatter.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { ChartOptions, ChartData } from 'chart.js'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
|
||||
let zoomable = false
|
||||
let pannable = false
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
@@ -59,18 +66,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as ChartOptions<'scatter'>
|
||||
|
||||
$: data = {
|
||||
datasets: result ?? []
|
||||
}
|
||||
} as ChartData<'scatter', (number | Point)[], unknown>
|
||||
|
||||
$: css = concatCustomCss($app.css?.scatterchartcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
|
||||
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
{/if}
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
{/if}
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
@@ -7,6 +7,9 @@
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
@@ -14,10 +17,12 @@
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'text'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
|
||||
let extraStyle: string | undefined = undefined
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let result: string | undefined = undefined
|
||||
let style: 'Title' | 'Subtitle' | 'Body' | 'Caption' | 'Label' | undefined = undefined
|
||||
let copyButton: boolean
|
||||
@@ -56,7 +61,6 @@
|
||||
$: style && (classes = getClasses())
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.extraStyle} bind:value={extraStyle} />
|
||||
<InputValue {id} input={configuration.style} bind:value={style} />
|
||||
<InputValue {id} input={configuration.copyButton} bind:value={copyButton} />
|
||||
|
||||
@@ -68,7 +72,16 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-wrap gap-2 pb-0.5 overflow-x-auto">
|
||||
<svelte:element this={component} class="whitespace-pre-wrap {classes}" style={extraStyle}>
|
||||
<svelte:element
|
||||
this={component}
|
||||
class={twMerge(
|
||||
'whitespace-pre-wrap',
|
||||
$app.css?.['textcomponent']?.['text']?.class,
|
||||
customCss?.text?.class,
|
||||
classes
|
||||
)}
|
||||
style={[$app.css?.['textcomponent']?.['text']?.style, customCss?.text?.style].join(';')}
|
||||
>
|
||||
{String(result)}
|
||||
</svelte:element>
|
||||
{#if copyButton && result}
|
||||
@@ -12,19 +12,26 @@
|
||||
CategoryScale,
|
||||
BarElement,
|
||||
TimeScale,
|
||||
LogarithmicScale
|
||||
LogarithmicScale,
|
||||
type Point
|
||||
} from 'chart.js'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import Scatter from 'svelte-chartjs/Scatter.svelte'
|
||||
import { Scatter } from 'svelte-chartjs'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { ChartOptions, ChartData } from 'chart.js'
|
||||
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
const { app } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let logarithmicScale = false
|
||||
let zoomable = false
|
||||
@@ -73,11 +80,13 @@
|
||||
type: logarithmicScale ? 'logarithmic' : 'linear'
|
||||
}
|
||||
}
|
||||
}
|
||||
} as ChartOptions<'scatter'>
|
||||
|
||||
$: data = {
|
||||
datasets: result ?? []
|
||||
}
|
||||
} as ChartData<'scatter', (number | Point)[], unknown>
|
||||
|
||||
$: css = concatCustomCss($app.css?.timeseriescomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.logarithmicScale} bind:value={logarithmicScale} />
|
||||
@@ -85,7 +94,9 @@
|
||||
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
|
||||
|
||||
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
{/if}
|
||||
<div class="w-full h-full {css?.container?.class ?? ''}" style={css?.container?.style ?? ''}>
|
||||
{#if result}
|
||||
<Scatter {data} {options} />
|
||||
{/if}
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
@@ -1,12 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
// export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
@@ -42,11 +41,6 @@
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if !Plotly}
|
||||
<div class="p-2">
|
||||
<Loader2 class="animate-spin" />
|
||||
</div>
|
||||
{/if}
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -54,11 +53,6 @@
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if !vegaEmbed}
|
||||
<div class="p-2">
|
||||
<Loader2 class="animate-spin" />
|
||||
</div>
|
||||
{/if}
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
12
frontend/src/lib/components/apps/components/display/index.ts
Normal file
12
frontend/src/lib/components/apps/components/display/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export { default as AppTable } from './table/AppTable.svelte'
|
||||
export { default as AppBarChart } from './AppBarChart.svelte'
|
||||
export { default as AppDisplayComponent } from './AppDisplayComponent.svelte'
|
||||
export { default as AppHtml } from './AppHtml.svelte'
|
||||
export { default as AppIcon } from './AppIcon.svelte'
|
||||
export { default as AppImage } from './AppImage.svelte'
|
||||
export { default as AppPieChart } from './AppPieChart.svelte'
|
||||
export { default as AppScatterChart } from './AppScatterChart.svelte'
|
||||
export { default as AppText } from './AppText.svelte'
|
||||
export { default as AppTimeseries } from './AppTimeseries.svelte'
|
||||
export { default as PlotlyHtml } from './PlotlyHtml.svelte'
|
||||
export { default as VegaLiteHtml } from './VegaLiteHtml.svelte'
|
||||
@@ -0,0 +1,118 @@
|
||||
<script lang="ts">
|
||||
import AgGridSvelte from 'ag-grid-svelte/AgGridSvelte.svelte'
|
||||
import 'ag-grid-community/styles/ag-grid.css'
|
||||
import 'ag-grid-community/styles/ag-theme-alpine.css'
|
||||
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppEditorContext } from '../../../types'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
|
||||
import { isObject } from '$lib/utils'
|
||||
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let initializing: boolean | undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['selectedRow', 'loading', 'result', 'selectedRowIndex']
|
||||
|
||||
let result: Record<string, any>[] | undefined = undefined
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
staticOutputs: staticOutputsStore,
|
||||
selectedComponent
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let selectedRowIndex = -1
|
||||
|
||||
function toggleRow(row: Record<string, any>, rowIndex: number) {
|
||||
if (selectedRowIndex !== rowIndex) {
|
||||
selectedRowIndex = rowIndex
|
||||
outputs?.selectedRow.set(row.original)
|
||||
outputs?.selectedRowIndex.set(rowIndex)
|
||||
}
|
||||
}
|
||||
|
||||
let mounted = false
|
||||
onMount(() => {
|
||||
mounted = true
|
||||
})
|
||||
|
||||
$: selectedRowIndex === -1 &&
|
||||
Array.isArray(result) &&
|
||||
result.length > 0 &&
|
||||
// We need to wait until the component is mounted so the world is created
|
||||
mounted &&
|
||||
outputs &&
|
||||
toggleRow({ original: result[0] }, 0)
|
||||
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
selectedRowIndex: Output<number>
|
||||
selectedRow: Output<any>
|
||||
result: Output<any>
|
||||
}
|
||||
|
||||
$: outputs?.result?.set(result)
|
||||
|
||||
let clientHeight
|
||||
let clientWidth
|
||||
|
||||
let columnDefs: any = undefined
|
||||
|
||||
let allEditable: boolean | undefined = undefined
|
||||
let pagination: boolean | undefined = undefined
|
||||
let pageSize: number | undefined = 10
|
||||
|
||||
function onCellValueChanged(event) {
|
||||
if (result) {
|
||||
let dataCell = event.newValue
|
||||
try {
|
||||
dataCell = JSON.parse(dataCell)
|
||||
} catch (e) {}
|
||||
result[event.node.rowIndex][event.column.colId] = dataCell
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.columnDefs} bind:value={columnDefs} />
|
||||
<InputValue {id} input={configuration.allEditable} bind:value={allEditable} />
|
||||
<InputValue {id} input={configuration.pagination} bind:value={pagination} />
|
||||
<InputValue {id} input={configuration.pageSize} bind:value={pageSize} />
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if Array.isArray(result) && result.every(isObject)}
|
||||
<div
|
||||
class="border border-gray-300 shadow-sm divide-y divide-gray-300 flex flex-col h-full"
|
||||
bind:clientHeight
|
||||
bind:clientWidth
|
||||
>
|
||||
<div
|
||||
on:pointerdown|stopPropagation={() => {
|
||||
$selectedComponent = id
|
||||
}}
|
||||
style:height="{clientHeight}px"
|
||||
style:width="{clientWidth}px"
|
||||
class="ag-theme-alpine"
|
||||
>
|
||||
{#key pagination}
|
||||
<AgGridSvelte
|
||||
bind:rowData={result}
|
||||
{columnDefs}
|
||||
{pagination}
|
||||
paginationPageSize={pageSize}
|
||||
defaultColDef={{ flex: 1, editable: allEditable, onCellValueChanged }}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
</div>
|
||||
{:else if result != undefined}
|
||||
<Alert title="Parsing issues" type="error" size="xs">
|
||||
The result should be an array of objects
|
||||
</Alert>
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
@@ -1,25 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppEditorContext, BaseAppComponent } from '../../types'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppEditorContext, BaseAppComponent, ComponentCustomCSS } from '../../../types'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { createSvelteTable, flexRender, type TableOptions } from '@tanstack/svelte-table'
|
||||
import AppButton from '../buttons/AppButton.svelte'
|
||||
import AppButton from '../../buttons/AppButton.svelte'
|
||||
import { classNames, isObject } from '$lib/utils'
|
||||
import DebouncedInput from '../helpers/DebouncedInput.svelte'
|
||||
import DebouncedInput from '../../helpers/DebouncedInput.svelte'
|
||||
import AppTableFooter from './AppTableFooter.svelte'
|
||||
import { tableOptions } from './tableOptions'
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
import type { ButtonComponent } from '../../editor/Component.svelte'
|
||||
import type { ButtonComponent } from '../../../editor/component'
|
||||
import { concatCustomCss } from '../../../utils'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let actionButtons: (BaseAppComponent & ButtonComponent)[]
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss:
|
||||
| ComponentCustomCSS<'container' | 'tableHeader' | 'tableBody' | 'tableFooter'>
|
||||
| undefined = undefined
|
||||
|
||||
export const staticOutputs: string[] = [
|
||||
'selectedRow',
|
||||
@@ -51,8 +56,11 @@
|
||||
|
||||
let table = createSvelteTable(options)
|
||||
|
||||
const { worldStore, staticOutputs: staticOutputsStore } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
const {
|
||||
app,
|
||||
worldStore,
|
||||
staticOutputs: staticOutputsStore
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let selectedRowIndex = -1
|
||||
|
||||
@@ -133,13 +141,22 @@
|
||||
}
|
||||
|
||||
$: result && rerender()
|
||||
|
||||
$: css = concatCustomCss($app.css?.tablecomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.search} bind:value={search} />
|
||||
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
|
||||
{#if Array.isArray(result) && result.every(isObject)}
|
||||
<div class="border border-gray-300 shadow-sm divide-y divide-gray-300 flex flex-col h-full">
|
||||
<div
|
||||
class={twMerge(
|
||||
'border border-gray-300 shadow-sm divide-y divide-gray-300 h-full',
|
||||
css?.container?.class ?? '',
|
||||
'flex flex-col'
|
||||
)}
|
||||
style={css?.container?.style ?? ''}
|
||||
>
|
||||
{#if search !== 'Disabled'}
|
||||
<div class="px-2 py-1">
|
||||
<div class="flex items-center">
|
||||
@@ -152,7 +169,14 @@
|
||||
|
||||
<div class="overflow-x-auto flex-1 w-full">
|
||||
<table class="relative w-full border-b border-b-gray-200">
|
||||
<thead class="sticky top-0 z-40 bg-gray-50 text-left">
|
||||
<thead
|
||||
class={twMerge(
|
||||
'bg-gray-50 text-left',
|
||||
css?.tableHeader?.class ?? '',
|
||||
'sticky top-0 z-40'
|
||||
)}
|
||||
style={css?.tableHeader?.style ?? ''}
|
||||
>
|
||||
{#each $table.getHeaderGroups() as headerGroup}
|
||||
<tr class="divide-x">
|
||||
{#each headerGroup.headers as header}
|
||||
@@ -178,7 +202,10 @@
|
||||
</tr>
|
||||
{/each}
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white ">
|
||||
<tbody
|
||||
class={twMerge('divide-y divide-gray-200 bg-white', css?.tableBody?.class ?? '')}
|
||||
style={css?.tableBody?.style ?? ''}
|
||||
>
|
||||
{#each $table.getRowModel().rows as row, rowIndex (row.id)}
|
||||
<tr
|
||||
class={classNames(
|
||||
@@ -235,7 +262,13 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<AppTableFooter paginationEnabled={pagination} {result} {table} />
|
||||
<AppTableFooter
|
||||
paginationEnabled={pagination}
|
||||
{result}
|
||||
{table}
|
||||
class={css?.tableFooter.class}
|
||||
style={css?.tableFooter.style}
|
||||
/>
|
||||
</div>
|
||||
{:else if result != undefined}
|
||||
<Alert title="Parsing issues" type="error" size="xs">
|
||||
@@ -4,6 +4,7 @@
|
||||
import type { Table } from '@tanstack/svelte-table'
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-svelte'
|
||||
import type { Readable } from 'svelte/store'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { tableOptions } from './tableOptions'
|
||||
|
||||
type T = Record<string, any>
|
||||
@@ -11,6 +12,9 @@
|
||||
export let result: Array<T>
|
||||
export let paginationEnabled: boolean = false
|
||||
export let table: Readable<Table<T>>
|
||||
let c = ''
|
||||
export { c as class }
|
||||
export let style = ''
|
||||
|
||||
function downloadResultAsJSON() {
|
||||
const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(result))
|
||||
@@ -23,7 +27,10 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="px-2 py-1 text-xs flex flex-row gap-2 items-center justify-between">
|
||||
<div
|
||||
class={twMerge('px-2 py-1 text-xs gap-2 items-center justify-between', c, 'flex flex-row')}
|
||||
{style}
|
||||
>
|
||||
{#if paginationEnabled && result.length > (tableOptions.initialState?.pagination?.pageSize ?? 25)}
|
||||
<div class="flex items-center gap-2 flex-row">
|
||||
<Button
|
||||
@@ -1,30 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { classNames } from '$lib/utils'
|
||||
import type { HorizontalAlignment, VerticalAlignment } from '../../types'
|
||||
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let horizontalAlignment: HorizontalAlignment | undefined = undefined
|
||||
export let verticalAlignment: VerticalAlignment | undefined = undefined
|
||||
export let noWFull = false
|
||||
|
||||
function tailwindHorizontalAlignment(horizontalAlignment) {
|
||||
switch (horizontalAlignment) {
|
||||
case 'left':
|
||||
return 'justify-start'
|
||||
case 'center':
|
||||
return 'justify-center'
|
||||
case 'right':
|
||||
return 'justify-end'
|
||||
function tailwindHorizontalAlignment(alignment?: HorizontalAlignment) {
|
||||
if(!alignment) return '';
|
||||
const classes: Record<HorizontalAlignment, string> = {
|
||||
left: 'justify-start',
|
||||
center: 'justify-center',
|
||||
right: 'justify-end',
|
||||
}
|
||||
return classes[alignment]
|
||||
}
|
||||
|
||||
function tailwindVerticalAlignment(verticalAlignment) {
|
||||
switch (verticalAlignment) {
|
||||
case 'top':
|
||||
return 'items-start'
|
||||
case 'center':
|
||||
return 'items-center'
|
||||
case 'bottom':
|
||||
return 'items-end'
|
||||
function tailwindVerticalAlignment(alignment?: VerticalAlignment) {
|
||||
if(!alignment) return '';
|
||||
const classes: Record<VerticalAlignment, string> = {
|
||||
top: 'items-start',
|
||||
center: 'items-center',
|
||||
bottom: 'items-end',
|
||||
}
|
||||
return classes[alignment]
|
||||
}
|
||||
|
||||
$: classes = classNames(
|
||||
@@ -32,7 +31,8 @@
|
||||
noWFull ? '' : 'w-full',
|
||||
tailwindHorizontalAlignment(horizontalAlignment),
|
||||
tailwindVerticalAlignment(verticalAlignment),
|
||||
verticalAlignment ? 'h-full' : ''
|
||||
verticalAlignment ? 'h-full' : '',
|
||||
$$props.class || ''
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { ConnectedAppInput, RowAppInput, StaticAppInput, UserAppInput } from '../inputType'
|
||||
import type { InlineScript } from '../types'
|
||||
import RunnableComponent from './helpers/RunnableComponent.svelte'
|
||||
import type { ConnectedAppInput, RowAppInput, StaticAppInput, UserAppInput } from '../../inputType'
|
||||
import type { InlineScript } from '../../types'
|
||||
import RunnableComponent from './RunnableComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let name: string
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { isCodeInjection } from '$lib/components/flows/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput, EvalAppInput } from '../../inputType'
|
||||
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { accessPropertyByPath } from '../../utils'
|
||||
|
||||
@@ -10,25 +10,23 @@
|
||||
export let input: AppInput
|
||||
export let value: T
|
||||
export let id: string | undefined = undefined
|
||||
export let row: Record<string, any> | undefined = {}
|
||||
export let error: string = ''
|
||||
|
||||
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
$: state = $worldStore?.state
|
||||
$: input && $worldStore && row && handleConnection()
|
||||
$: input && $worldStore && handleConnection()
|
||||
$: input && input.type == 'template' && $state && (value = getValue(input))
|
||||
$: input && input.type == 'eval' && $state && (value = evalExpr(input))
|
||||
|
||||
function handleConnection() {
|
||||
if (input.type === 'connected') {
|
||||
$worldStore?.connect<any>(input, onValueChange)
|
||||
} else if (input.type === 'row') {
|
||||
setTimeout(() => (value = row?.[input['column']]), 0)
|
||||
} else if (input.type === 'static' || input.type == 'template') {
|
||||
setTimeout(() => (value = getValue(input)), 0)
|
||||
} else if (input.type == 'eval') {
|
||||
setTimeout(() => ((value = evalExpr(input as EvalAppInput)), 0))
|
||||
} else if (input.type == 'upload') {
|
||||
setTimeout(() => ((value = (input as UploadAppInput).value), 0))
|
||||
} else {
|
||||
setTimeout(() => (value = undefined), 0)
|
||||
}
|
||||
@@ -55,7 +53,6 @@
|
||||
Object.fromEntries(Object.entries(value ?? {}).map((x) => [x[0], x[1].peak()]))
|
||||
]
|
||||
})
|
||||
.concat(row ? [['row', row]] : [])
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
export let defaultUserInput = false
|
||||
export let flexWrap = false
|
||||
export let wrapperClass = ''
|
||||
export let wrapperStyle = ''
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let gotoUrl: string | undefined = undefined
|
||||
|
||||
@@ -239,12 +240,7 @@
|
||||
|
||||
{#each Object.entries(fields ?? {}) as [key, v]}
|
||||
{#if v.type != 'static' && v.type != 'user'}
|
||||
<InputValue
|
||||
{id}
|
||||
input={fields[key]}
|
||||
bind:value={runnableInputValues[key]}
|
||||
row={extraQueryParams['row'] ?? {}}
|
||||
/>
|
||||
<InputValue {id} input={fields[key]} bind:value={runnableInputValues[key]} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -275,7 +271,7 @@
|
||||
bind:this={testJobLoader}
|
||||
/>
|
||||
|
||||
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}">
|
||||
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}" style={wrapperStyle}>
|
||||
{#if !initializing && autoRefresh === true}
|
||||
<div class="flex absolute top-1 right-1">
|
||||
<RefreshButton componentId={id} />
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
export let defaultUserInput = false
|
||||
export let flexWrap = false
|
||||
export let runnableClass = ''
|
||||
export let runnableStyle = ''
|
||||
export let goto: string | undefined = undefined
|
||||
|
||||
const { staticExporter, noBackend } = getContext<AppEditorContext>('AppEditorContext')
|
||||
@@ -55,6 +56,7 @@
|
||||
{forceSchemaDisplay}
|
||||
{initializing}
|
||||
wrapperClass={runnableClass}
|
||||
wrapperStyle={runnableStyle}
|
||||
>
|
||||
<slot />
|
||||
</RunnableComponent>
|
||||
|
||||
10
frontend/src/lib/components/apps/components/helpers/index.ts
Normal file
10
frontend/src/lib/components/apps/components/helpers/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export { default as AlignWrapper } from './AlignWrapper.svelte'
|
||||
export { default as DebouncedInput } from './DebouncedInput.svelte'
|
||||
export { default as HiddenComponent } from './HiddenComponent.svelte'
|
||||
export { default as InputDefaultValue } from './InputDefaultValue.svelte'
|
||||
export { default as InputValue } from './InputValue.svelte'
|
||||
export { default as MissingConnectionWarning } from './MissingConnectionWarning.svelte'
|
||||
export { default as NonRunnableComponent } from './NonRunnableComponent.svelte'
|
||||
export { default as RefreshButton } from './RefreshButton.svelte'
|
||||
export { default as RunnableComponent } from './RunnableComponent.svelte'
|
||||
export { default as RunnableWrapper } from './RunnableWrapper.svelte'
|
||||
21
frontend/src/lib/components/apps/components/icon.ts
Normal file
21
frontend/src/lib/components/apps/components/icon.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { toKebabCase } from '../utils'
|
||||
|
||||
export async function loadIcon(name: string): Promise<any> {
|
||||
let iconComponent: any
|
||||
try {
|
||||
if (name) {
|
||||
name = toKebabCase(name).replace(/([a-z])(\d)/i, '$1-$2')
|
||||
iconComponent = (
|
||||
await import(
|
||||
`../../../../../node_modules/lucide-svelte/dist/svelte/icons/${name}.svelte`
|
||||
)
|
||||
).default
|
||||
return iconComponent
|
||||
} else {
|
||||
iconComponent = undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
iconComponent = undefined
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user