@@ -4,6 +4,20 @@
|
||||
|
||||
Shuffle Installation
|
||||
|
||||
<div>
|
||||
<a
|
||||
href="https://console.cloud.google.com/marketplace/product/shuffle-public/shuffle"
|
||||
target="_blank"
|
||||
style="text-decoration: none; display: flex; align-items: center; gap: 8px;"
|
||||
>
|
||||
<img
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/5/51/Google_Cloud_logo.svg"
|
||||
height="20"
|
||||
alt="Google Cloud Platform"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</h1>
|
||||
|
||||
Installation of Shuffle is currently available for [docker](https://shuffler.io/docs/configuration#production-readiness) and [kubernetes](https://shuffler.io/docs/configuration#Kubernetes).
|
||||
@@ -13,6 +27,7 @@ Installation of Shuffle is currently available for [docker](https://shuffler.io/
|
||||
|
||||
This document outlines an introduction environment which is **not** scalable. [Read here](https://shuffler.io/docs/configuration#production_readiness) for information on production readiness and scalability. This also includes system requirements and configurations for **Docker Swarm** or **Kubernetes**.
|
||||
|
||||
|
||||
# Docker - *nix
|
||||
The Docker setup is the default setup, and is ran with docker compose. This is [NOT a scalable build](https://shuffler.io/docs/configuration#production-readiness) without changes.
|
||||
|
||||
|
||||
@@ -29,4 +29,4 @@ shuffle-database/nodes
|
||||
shuffle-database/performance_analyzer_enabled.conf
|
||||
shuffle-database/rca_enabled.conf
|
||||
|
||||
*/package-lock.json
|
||||
#*/package-lock.json
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
[](https://shuffler.io)
|
||||
|
||||
Shuffle Automation
|
||||
<div>
|
||||
<a
|
||||
href="https://console.cloud.google.com/marketplace/product/shuffle-public/shuffle"
|
||||
target="_blank"
|
||||
style="text-decoration: none; display: flex; align-items: center; gap: 8px;"
|
||||
>
|
||||
<img
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/5/51/Google_Cloud_logo.svg"
|
||||
height="20"
|
||||
alt="Google Cloud Platform"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</h1><h4 align="center">
|
||||
|
||||
@@ -24,6 +37,10 @@ Follow us on Twitter at [@shuffleio](https://twitter.com/shuffleio).
|
||||
|
||||

|
||||
|
||||
## Deployment
|
||||
|
||||
**Shuffle can be deployed using the following cloud marketplaces:**
|
||||
|
||||
## Try it
|
||||
* Self-hosted: Check out the [installation guide](https://github.com/shuffle/shuffle/blob/master/.github/install-guide.md)
|
||||
* Cloud: Register at https://shuffler.io/register and get cooking
|
||||
|
||||
@@ -415,7 +415,7 @@ func buildImage(tags []string, dockerfileLocation string) error {
|
||||
MountPath: "/app/generated",
|
||||
},
|
||||
{
|
||||
Name: "docker-config",
|
||||
Name: "docker-config",
|
||||
MountPath: "/kaniko/.docker/",
|
||||
},
|
||||
},
|
||||
@@ -439,8 +439,8 @@ func buildImage(tags []string, dockerfileLocation string) error {
|
||||
SecretName: os.Getenv("SHUFFLE_REGISTRY_SECRET"),
|
||||
Items: []corev1.KeyToPath{
|
||||
{
|
||||
Key: ".dockerconfigjson",
|
||||
Path: "config.json",
|
||||
Key: ".dockerconfigjson",
|
||||
Path: "config.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -22,12 +22,12 @@ require (
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-git/go-billy/v5 v5.6.2
|
||||
github.com/go-git/go-git/v5 v5.16.1
|
||||
github.com/go-git/go-git/v5 v5.16.5
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.9.69
|
||||
github.com/shuffle/singul v0.0.20
|
||||
github.com/shuffle/shuffle-shared v0.9.87
|
||||
github.com/shuffle/singul v0.0.26
|
||||
golang.org/x/crypto v0.45.0
|
||||
google.golang.org/api v0.236.0
|
||||
google.golang.org/grpc v1.72.2
|
||||
@@ -65,6 +65,7 @@ require (
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
@@ -75,15 +76,16 @@ require (
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/frikky/schemaless v0.0.25 // indirect
|
||||
github.com/frikky/schemaless v0.0.28 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
|
||||
@@ -101,6 +101,8 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
@@ -120,8 +122,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -138,12 +140,12 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
|
||||
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/frikky/schemaless v0.0.25 h1:qXjrKT54LWl3tzDFIUmevZ86F7FiusPpQKfa40xxNl8=
|
||||
github.com/frikky/schemaless v0.0.25/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/frikky/schemaless v0.0.28 h1:gdurMqBwtvY4Y/5pcxn8bdGCJn/eolKGz+c5DcidLkI=
|
||||
github.com/frikky/schemaless v0.0.28/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/fsouza/go-dockerclient v1.12.1 h1:FMoLq+Zhv9Oz/rFmu6JWkImfr6CBgZOPcL+bHW4gS0o=
|
||||
github.com/fsouza/go-dockerclient v1.12.1/go.mod h1:OqsgJJcpCwqyM3JED7TdfM9QVWS5O7jSYwXxYKmOooY=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
@@ -156,12 +158,12 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.1 h1:TuxMBWNL7R05tXsUGi0kh1vi4tq0WfXNLlIrAkXG1k8=
|
||||
github.com/go-git/go-git/v5 v5.16.1/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
|
||||
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
@@ -179,6 +181,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -201,15 +205,14 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
|
||||
@@ -294,8 +297,9 @@ github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFL
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@@ -304,6 +308,8 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/openai/openai-go/v3 v3.8.1 h1:b+YWsmwqXnbpSHWQEntZAkKciBZ5CJXwL68j+l59UDg=
|
||||
github.com/openai/openai-go/v3 v3.8.1/go.mod h1:UOpNxkqC9OdNXNUfpNByKOtB4jAL0EssQXq5p8gO0Xs=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
@@ -344,10 +350,10 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0 h1:Mh85CD1MwOgXiFFYlzS1llnvdqL3CztRdR1ZT/SLIjU=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0/go.mod h1:gVLZKQE5khQWMb68XBtgKrhu78oLGL2zHwAGnFMDwC0=
|
||||
github.com/shuffle/shuffle-shared v0.9.60 h1:8NiovcsSsVX8i0UHmbFBANjUQ/u+4PDDpJXg9JfMAUo=
|
||||
github.com/shuffle/shuffle-shared v0.9.60/go.mod h1:vK6t1WY5Nfg5vOAk6taT788jIGKs6/4iN1d8Argyn4o=
|
||||
github.com/shuffle/singul v0.0.20 h1:Lz+K4l2GJQ5W6o8ePr/2qlIcGuZuMC7W8Wj/JFw1XDI=
|
||||
github.com/shuffle/singul v0.0.20/go.mod h1:qY3ZmwaNwmRApLWMTqZaNEMXO6X8bjr/zY44xDU62hQ=
|
||||
github.com/shuffle/shuffle-shared v0.9.87 h1:INA1cZ18MKcMs8kaVBJoQqrdNuqLHfge9elJ17hZfVc=
|
||||
github.com/shuffle/shuffle-shared v0.9.87/go.mod h1:AkXajlWWB16WfWjCw9K7y38L8JKABJzVxcX6KI/J1H4=
|
||||
github.com/shuffle/singul v0.0.26 h1:P2uZ8YIYQUN4qNfQujoW3Lhod91X6TAfrRHGWiUPNI8=
|
||||
github.com/shuffle/singul v0.0.26/go.mod h1:S8GszXL+fT2mTnh7j57V0r/tY5Y/mSW8QowQEnADs3k=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
@@ -376,10 +382,12 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
@@ -427,6 +435,10 @@ go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9f
|
||||
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -436,8 +448,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -484,8 +496,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -501,8 +513,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -526,13 +538,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -542,8 +554,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
@@ -575,8 +587,8 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -656,27 +668,26 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw=
|
||||
k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw=
|
||||
k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
|
||||
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4=
|
||||
k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA=
|
||||
k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY=
|
||||
k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw=
|
||||
k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4=
|
||||
k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M=
|
||||
k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/shuffle/shuffle-shared"
|
||||
singul "github.com/shuffle/singul/pkg"
|
||||
|
||||
"net/http/pprof"
|
||||
//"net/http/pprof"
|
||||
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
@@ -39,7 +39,6 @@ import (
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
gitProxy "github.com/go-git/go-git/v5/plumbing/transport"
|
||||
http2 "github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
|
||||
@@ -79,6 +78,7 @@ type retStruct struct {
|
||||
Reason string `json:"reason"`
|
||||
Subscriptions []shuffle.PaymentSubscription `json:"subscriptions"`
|
||||
Licensed bool `json:"licensed"`
|
||||
CloudSyncUrl string `json:"cloud_sync_url,omitempty"`
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
@@ -409,52 +409,6 @@ func checkUsername(Username string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isGitNoProxy(rawURL string) bool {
|
||||
noProxy := os.Getenv("NO_PROXY")
|
||||
if noProxy == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if noProxy == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
noProxyList := strings.Split(noProxy, ",")
|
||||
parsedURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
host := parsedURL.Hostname()
|
||||
|
||||
for _, value := range noProxyList {
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
if host == value {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(value, "*.") && strings.HasSuffix(host, value[2:]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkGitProxy(cloneOptions *git.CloneOptions) *git.CloneOptions {
|
||||
if os.Getenv("HTTP_PROXY") != "" && !isGitNoProxy(cloneOptions.URL) {
|
||||
cloneOptions.ProxyOptions = gitProxy.ProxyOptions{
|
||||
URL: os.Getenv("HTTP_PROXY"),
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getenv("HTTPS_PROXY") != "" && !isGitNoProxy(cloneOptions.URL) {
|
||||
cloneOptions.ProxyOptions = gitProxy.ProxyOptions{
|
||||
URL: os.Getenv("HTTPS_PROXY"),
|
||||
}
|
||||
}
|
||||
|
||||
return cloneOptions
|
||||
}
|
||||
|
||||
func createNewUser(username, password, role, apikey string, org shuffle.OrgMini) error {
|
||||
// Returns false if there is an issue
|
||||
// Use this for register
|
||||
@@ -826,11 +780,8 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
|
||||
//}
|
||||
|
||||
expiration := time.Now().Add(3600 * time.Second)
|
||||
http.SetCookie(resp, &http.Cookie{
|
||||
Name: "session_token",
|
||||
Value: userInfo.Session,
|
||||
Expires: expiration,
|
||||
})
|
||||
sessionCookie := shuffle.ConstructSessionCookie(userInfo.Session, expiration)
|
||||
http.SetCookie(resp, sessionCookie)
|
||||
|
||||
// Updating user info if there's something wrong
|
||||
if len(userInfo.ActiveOrg.Name) == 0 || len(userInfo.ActiveOrg.Id) == 0 {
|
||||
@@ -1312,7 +1263,7 @@ func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
// Should run calculations
|
||||
if len(org.SSOConfig.OpenIdAuthorization) > 0 {
|
||||
baseSSOUrl = shuffle.GetOpenIdUrl(request, *org)
|
||||
baseSSOUrl, err = shuffle.GetOpenIdUrl(request, *org, user, "")
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed getting OpenID URL for org %s: %s", org.Name, err)
|
||||
}
|
||||
@@ -3909,6 +3860,7 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error {
|
||||
SyncFeatures shuffle.SyncFeatures `json:"sync_features"`
|
||||
Subscriptions []shuffle.PaymentSubscription `json:"subscriptions"`
|
||||
Licensed bool `json:"licensed"`
|
||||
CloudSyncUrl string `json:"cloud_sync_url,omitempty"`
|
||||
}
|
||||
|
||||
responseData := retStruct{}
|
||||
@@ -4086,6 +4038,19 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
||||
backupJobData = []byte{}
|
||||
}
|
||||
|
||||
cloudSyncRegionUrlCacheKey := fmt.Sprintf("org_cloudsync_region_url_%s", org.Id)
|
||||
cloudSyncRegionUrlCached, err := shuffle.GetCache(ctx, cloudSyncRegionUrlCacheKey)
|
||||
if err == nil {
|
||||
if cachedBytes, ok := cloudSyncRegionUrlCached.([]byte); ok && len(cachedBytes) > 0 {
|
||||
syncUrl = string(cachedBytes)
|
||||
log.Printf("[DEBUG] Using cached cloud sync region url for org %s: %s", org.Id, syncUrl)
|
||||
}
|
||||
}
|
||||
|
||||
if len(syncUrl) == 0 || !strings.HasPrefix(syncUrl, "http") {
|
||||
syncUrl = "https://shuffler.io"
|
||||
}
|
||||
|
||||
syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl)
|
||||
client := shuffle.GetExternalClient(syncUrl)
|
||||
req, err := http.NewRequest(
|
||||
@@ -4118,6 +4083,437 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// /api/v1/mcp
|
||||
// /api/v1/agent
|
||||
// /api/v1/apps/{appid}/mcp
|
||||
func runMCPAction(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := shuffle.HandleCors(resp, request)
|
||||
if cors {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := shuffle.GetContext(request)
|
||||
parentExec := shuffle.WorkflowExecution{}
|
||||
user, err := shuffle.HandleApiAuthentication(resp, request)
|
||||
if err != nil {
|
||||
// Look for org_id query as app may be private
|
||||
// No validation is done here, as it's just running the app
|
||||
// to find a user
|
||||
orgId := request.URL.Query().Get("org_id")
|
||||
if len(orgId) > 0 {
|
||||
user.ActiveOrg.Id = orgId
|
||||
} else {
|
||||
executionId := request.URL.Query().Get("execution_id")
|
||||
authorization := request.URL.Query().Get("authorization")
|
||||
if len(executionId) == 0 || len(authorization) == 0 {
|
||||
log.Printf("[WARNING] Bad execution id/auth in single action validate (1): %#v, %#v. Continuing with the 'public' org id", executionId, authorization)
|
||||
err := shuffle.ValidateRequestOverload(resp, request)
|
||||
if err != nil {
|
||||
log.Printf("[INFO] Request overload for IP %s in single action execution", shuffle.GetRequestIp(request))
|
||||
resp.WriteHeader(429)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Too many requests. Please try again in 30 seconds."}`)))
|
||||
return
|
||||
}
|
||||
|
||||
user.Username = shuffle.GetRequestIp(request)
|
||||
user.ActiveOrg.Name = shuffle.GetRequestIp(request)
|
||||
user.ActiveOrg.Id = "public"
|
||||
|
||||
} else {
|
||||
// Find the execution
|
||||
exec, err := shuffle.GetWorkflowExecution(ctx, executionId)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Bad execution id in single action validate (2): %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Bad execution mapping (1)"}`))
|
||||
return
|
||||
}
|
||||
|
||||
if exec.Authorization != authorization {
|
||||
log.Printf("[WARNING] Bad execution auth in single action validate (3): %#v, %#v", exec.Authorization, authorization)
|
||||
resp.WriteHeader(403)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Bad execution mapping (2)"}`))
|
||||
return
|
||||
}
|
||||
|
||||
parentExec = *exec
|
||||
user.ActiveOrg.Id = exec.OrgId
|
||||
if len(user.ActiveOrg.Id) == 0 {
|
||||
user.ActiveOrg.Id = exec.ExecutionOrg
|
||||
}
|
||||
|
||||
user.Username = fmt.Sprintf("org %s", user.ActiveOrg.Id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(user.ActiveOrg.Id) == 0 {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "reason": "No org_id found to map back to"}`))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
location := strings.Split(request.URL.Path, "/")
|
||||
var fileId string
|
||||
if location[1] == "api" {
|
||||
if len(location) <= 3 {
|
||||
resp.WriteHeader(400)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
if len(location) == 4 {
|
||||
fileId = location[3] // /api/v1/agent or /api/v1/mcp
|
||||
} else {
|
||||
fileId = location[4] // /api/v1/apps/{appid}/mcp
|
||||
}
|
||||
}
|
||||
|
||||
//log.Printf("[AUDIT] User Authentication failed in execute SINGLE action - CONTINUING ANYWAY: %s. Found OrgID: %#v", err, user.ActiveOrg.Id)
|
||||
log.Printf("[AUDIT] User %s (%s) in org %s (%s) is running SINGLE App run for App ID '%s'", user.Username, user.Id, user.ActiveOrg.Name, user.ActiveOrg.Id, fileId)
|
||||
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
log.Printf("[INFO] Failed single execution POST body read: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
foundRequest := shuffle.MCPRequest{}
|
||||
//func HandleAiAgentExecutionStart(execution WorkflowExecution, startNode Action, createNextActions bool) (Action, error) {
|
||||
// Unmarshal it
|
||||
err = json.Unmarshal(body, &foundRequest)
|
||||
if err != nil {
|
||||
log.Printf("[INFO] Failed single execution POST body unmarshal: %s", err)
|
||||
resp.WriteHeader(400)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
foundEnvironment := "Shuffle"
|
||||
if len(foundRequest.Params.Environment) > 0 {
|
||||
foundEnvironment = foundRequest.Params.Environment
|
||||
}
|
||||
|
||||
if len(foundRequest.Params.Input.Text) < 5 {
|
||||
resp.WriteHeader(400)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Input text is required and must be at least 5 characters"}`))
|
||||
return
|
||||
}
|
||||
|
||||
var newAction shuffle.Action
|
||||
|
||||
if fileId == "agent" {
|
||||
newAction = shuffle.Action{
|
||||
Name: "agent",
|
||||
AppName: "AI Agent",
|
||||
AppID: "shuffle_agent",
|
||||
AppVersion: "1.0.0",
|
||||
Environment: foundEnvironment,
|
||||
Parameters: []shuffle.WorkflowAppActionParameter{
|
||||
{
|
||||
Name: "app_name",
|
||||
Value: "openai",
|
||||
},
|
||||
{
|
||||
Name: "input",
|
||||
Value: foundRequest.Params.Input.Text,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
foundId := ""
|
||||
if len(foundRequest.Params.ToolID) > 0 {
|
||||
foundId = foundRequest.Params.ToolID
|
||||
} else {
|
||||
if len(foundRequest.Params.ToolName) == 32 {
|
||||
foundId = foundRequest.Params.ToolName
|
||||
} else {
|
||||
foundApps, err := shuffle.FindWorkflowAppByName(ctx, foundRequest.Params.ToolName)
|
||||
if err != nil || len(foundApps) == 0 {
|
||||
log.Printf("[INFO] Failed to find app by name '%s' in single execution: %s", foundRequest.Params.ToolName, err)
|
||||
resp.WriteHeader(400)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Valid param.tool_id (app ID) is required"}`))
|
||||
return
|
||||
}
|
||||
|
||||
for _, app := range foundApps {
|
||||
if app.Name == foundRequest.Params.ToolName {
|
||||
foundId = app.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app, err := shuffle.GetApp(ctx, foundId, shuffle.User{}, false)
|
||||
if err != nil {
|
||||
log.Printf("[INFO] Failed to find app by id '%s' in single execution: %s", foundId, err)
|
||||
resp.WriteHeader(400)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
if !app.Public {
|
||||
if user.Id == app.Owner || user.ActiveOrg.Id == app.ReferenceOrg || shuffle.ArrayContains(app.Contributors, user.Id) {
|
||||
log.Printf("[AUDIT] Support & Admin user %s (%s) got access to app %s (MCP)", user.Username, user.Id, app.ID)
|
||||
} else if user.Role == "admin" && app.Owner == "" {
|
||||
log.Printf("[AUDIT] Any admin can GET %s (%s), since it doesn't have an owner (GET - MCP).", app.Name, app.ID)
|
||||
} else {
|
||||
log.Printf("[AUDIT] User %s (%s) in org %s (%s) was denied access to app %s (MCP)", user.Username, user.Id, user.ActiveOrg.Name, user.ActiveOrg.Id, app.ID)
|
||||
resp.WriteHeader(403)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Printf("[AUDIT] User %s (%s) in org %s (%s) got access to public app %s (MCP)", user.Username, user.Id, user.ActiveOrg.Name, user.ActiveOrg.Id, app.ID)
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
parsedName := strings.ToLower(strings.ReplaceAll(app.Name, " ", "_"))
|
||||
parsedApp := fmt.Sprintf("app:%s:%s", app.ID, parsedName)
|
||||
|
||||
// Run the action
|
||||
newAction = shuffle.Action{
|
||||
Name: "agent",
|
||||
AppName: "AI Agent",
|
||||
AppID: "shuffle_agent",
|
||||
AppVersion: "1.0.0",
|
||||
Environment: foundEnvironment,
|
||||
Parameters: []shuffle.WorkflowAppActionParameter{
|
||||
{
|
||||
Name: "app_name",
|
||||
Value: "openai",
|
||||
},
|
||||
{
|
||||
Name: "input",
|
||||
Value: foundRequest.Params.Input.Text,
|
||||
},
|
||||
{
|
||||
Name: "app_name",
|
||||
Value: parsedApp,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
marshalledAction, err := json.Marshal(newAction)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to marshal single action body: %s", err)
|
||||
resp.WriteHeader(500)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
// Special handling for agent calls
|
||||
if fileId == "agent" {
|
||||
if len(parentExec.ExecutionId) > 0 {
|
||||
targetActionId := request.URL.Query().Get("action_id")
|
||||
agentNodeFound := false
|
||||
var agentNode shuffle.Action
|
||||
|
||||
for i, action := range parentExec.Workflow.Actions {
|
||||
if len(targetActionId) > 0 && action.ID != targetActionId {
|
||||
continue
|
||||
}
|
||||
if len(targetActionId) == 0 && action.AppName != "AI Agent" {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(foundRequest.Params.Input.Text) > 0 {
|
||||
for j, param := range parentExec.Workflow.Actions[i].Parameters {
|
||||
if param.Name == "input" {
|
||||
parentExec.Workflow.Actions[i].Parameters[j].Value = foundRequest.Params.Input.Text
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
agentNode = parentExec.Workflow.Actions[i]
|
||||
agentNodeFound = true
|
||||
log.Printf("[DEBUG][%s] AI Agent: Running AI Agent node '%s' (ID: %s)", parentExec.ExecutionId, agentNode.Label, agentNode.ID)
|
||||
break
|
||||
}
|
||||
|
||||
if !agentNodeFound {
|
||||
log.Printf("[ERROR][%s] No AI Agent node found in parent workflow (Target ID: %s)", parentExec.ExecutionId, targetActionId)
|
||||
resp.WriteHeader(400)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No AI Agent node found in parent workflow matching request"}`)))
|
||||
return
|
||||
}
|
||||
|
||||
go shuffle.HandleAiAgentExecutionStart(parentExec, agentNode, false)
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s", "mode": "hybrid"}`, parentExec.ExecutionId, parentExec.Authorization)))
|
||||
return
|
||||
|
||||
} else {
|
||||
//standalone mode
|
||||
workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, "agent_starter", marshalledAction, false, "")
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to prepare standalone agent execution: %s", err)
|
||||
resp.WriteHeader(500)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Failed to create agent execution"}`))
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Standalone agent execution created: %s", workflowExecution.ExecutionId)
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s", "mode": "standalone"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// For non-agent calls (MCP or other apps): continue with existing flow
|
||||
workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, "agent", marshalledAction, false, "")
|
||||
if fileId == "agent_starter" {
|
||||
log.Printf("[INFO] Returning early for agent_starter single action execution: %s", workflowExecution.ExecutionId)
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
|
||||
return
|
||||
}
|
||||
|
||||
debugUrl := fmt.Sprintf("/workflows/%s?execution_id=%s", workflowExecution.Workflow.ID, workflowExecution.ExecutionId)
|
||||
resp.Header().Add("X-Debug-Url", debugUrl)
|
||||
|
||||
if err != nil {
|
||||
returndata := shuffle.ResultChecker{
|
||||
Success: false,
|
||||
Reason: fmt.Sprintf("%s", err),
|
||||
}
|
||||
|
||||
// Special handler for decision reruns~
|
||||
if strings.Contains(err.Error(), "Successfully") {
|
||||
returndata.Success = true
|
||||
resp.WriteHeader(200)
|
||||
} else {
|
||||
log.Printf("[INFO] Failed workflowrequest POST read in single action (4): %s", err)
|
||||
resp.WriteHeader(400)
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(returndata)
|
||||
if err != nil {
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Write(respBytes)
|
||||
return
|
||||
}
|
||||
|
||||
foundEnv := ""
|
||||
params := []string{}
|
||||
for _, action := range workflowExecution.Workflow.Actions {
|
||||
for _, param := range action.Parameters {
|
||||
params = append(params, param.Name)
|
||||
}
|
||||
|
||||
if len(action.Environment) > 0 {
|
||||
foundEnv = action.Environment
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
go shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions")
|
||||
|
||||
executionRequest := shuffle.ExecutionRequest{
|
||||
ExecutionId: workflowExecution.ExecutionId,
|
||||
WorkflowId: workflowExecution.Workflow.ID,
|
||||
Authorization: workflowExecution.Authorization,
|
||||
Environments: []string{foundEnv},
|
||||
}
|
||||
|
||||
parsedEnv := fmt.Sprintf("%s_%s", strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(foundEnv, " ", "-"), "_", "-")), workflowExecution.ExecutionOrg)
|
||||
|
||||
// Check if environment is distributed from parent org
|
||||
if len(workflowExecution.ExecutionOrg) > 0 {
|
||||
environments, err := shuffle.GetEnvironments(ctx, workflowExecution.ExecutionOrg)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed getting environments for org %s in single action. May fail to verify env.: %s", workflowExecution.ExecutionOrg, err)
|
||||
} else {
|
||||
for _, env := range environments {
|
||||
if env.Archived {
|
||||
continue
|
||||
}
|
||||
|
||||
if env.Name != foundEnv {
|
||||
continue
|
||||
}
|
||||
|
||||
if env.OrgId != workflowExecution.ExecutionOrg && len(env.OrgId) > 0 {
|
||||
if debug {
|
||||
log.Printf("[DEBUG][%s] Found suborg environment %s for org %s in single action. Re-mapping it to org-id %s", workflowExecution.ExecutionId, env.Name, env.OrgId, env.OrgId)
|
||||
}
|
||||
|
||||
parsedEnv = fmt.Sprintf("%s_%s", strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(foundEnv, " ", "-"), "_", "-")), env.OrgId)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[INFO][%s] Adding new single-action job to env queue (4 - MCP): %s", workflowExecution.ExecutionId, parsedEnv)
|
||||
err = shuffle.SetWorkflowQueue(ctx, executionRequest, parsedEnv)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING][%s] Failed adding %s to db (single action queue): %s", workflowExecution.ExecutionId, parsedEnv, err)
|
||||
}
|
||||
|
||||
actionId := ""
|
||||
if len(workflowExecution.Workflow.Actions) == 1 {
|
||||
actionId = workflowExecution.Workflow.Actions[0].ID
|
||||
}
|
||||
|
||||
mappedResponse := shuffle.MCPResponse{
|
||||
Jsonrpc: foundRequest.Jsonrpc,
|
||||
ID: foundRequest.ID,
|
||||
}
|
||||
|
||||
singleResult := shuffle.HandleRetValidation(ctx, workflowExecution, 1, 45, actionId)
|
||||
agentOutput := shuffle.AgentOutput{}
|
||||
err = json.Unmarshal([]byte(singleResult.Result), &agentOutput)
|
||||
if err == nil && len(agentOutput.Output) > 0 {
|
||||
log.Printf("[INFO] Returning agent output in MCP response: %s", agentOutput.Output)
|
||||
|
||||
marshalledAgentOutput, err := json.Marshal(agentOutput)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to marshal agent output in MCP response: %s", err)
|
||||
} else {
|
||||
marshalledOutput := map[string]interface{}{}
|
||||
err = json.Unmarshal(marshalledAgentOutput, &marshalledOutput)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to unmarshal agent output in MCP response: %s", err)
|
||||
}
|
||||
|
||||
marshalledOutput["message"] = agentOutput.Output
|
||||
mappedResponse.Result = marshalledOutput
|
||||
}
|
||||
} else {
|
||||
marshalledSingleResult, err := json.Marshal(singleResult)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to marshal single result in MCP response: %s", err)
|
||||
}
|
||||
|
||||
marshalledResult := map[string]interface{}{}
|
||||
err = json.Unmarshal(marshalledSingleResult, &marshalledResult)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to unmarshal single result in MCP response: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
marshalledMappedResponse, err := json.Marshal(mappedResponse)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to marshal mapped response in MCP response: %s", err)
|
||||
resp.WriteHeader(500)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write(marshalledMappedResponse)
|
||||
}
|
||||
|
||||
func runInitCloudSetup() {
|
||||
action := shuffle.CloudSyncJob{
|
||||
Type: "setup",
|
||||
@@ -4597,7 +4993,7 @@ func runInitEs(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
cloneOptions = checkGitProxy(cloneOptions)
|
||||
cloneOptions = shuffle.CheckGitProxy(cloneOptions)
|
||||
|
||||
branch := os.Getenv("SHUFFLE_DOWNLOAD_AUTH_BRANCH")
|
||||
if len(branch) > 0 && branch != "master" && branch != "main" {
|
||||
@@ -4930,6 +5326,12 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) {
|
||||
//log.Printf("Apidata: %s", tmpData.Apikey)
|
||||
|
||||
// FIXME: Path
|
||||
|
||||
// just in case setup/stop URL is overwritten by region url
|
||||
if syncUrl != "https://shuffler.io" || syncUrl != "http://localhost:5002" {
|
||||
syncUrl = "https://shuffler.io"
|
||||
}
|
||||
|
||||
apiPath := "/api/v1/cloud/sync/setup"
|
||||
if tmpData.Disable {
|
||||
if !org.CloudSync {
|
||||
@@ -5078,6 +5480,11 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) {
|
||||
shuffle.SetCache(ctx, licenseCacheKey, licensedBytes, 1800)
|
||||
}
|
||||
|
||||
if len(responseData.CloudSyncUrl) > 0 {
|
||||
cloudSyncRegionUrlCacheKey := fmt.Sprintf("org_cloudsync_region_url_%s", org.Id)
|
||||
shuffle.SetCache(ctx, cloudSyncRegionUrlCacheKey, []byte(responseData.CloudSyncUrl), 1800)
|
||||
}
|
||||
|
||||
org.SyncConfig = shuffle.SyncConfig{
|
||||
Apikey: responseData.SessionKey,
|
||||
Interval: responseData.IntervalSeconds,
|
||||
@@ -5422,6 +5829,7 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/_ah/health", shuffle.HealthCheckHandler)
|
||||
r.HandleFunc("/api/v1/health", shuffle.RunOpsHealthCheck).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/health/stats", shuffle.GetOpsDashboardStats).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/health/opensearch-prefix", shuffle.HandleFixOpensearchPrefix).Methods("POST", "OPTIONS")
|
||||
|
||||
// Make user related locations
|
||||
// Fix user changes with org
|
||||
@@ -5479,6 +5887,12 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/apps/{key}/execute", executeSingleAction).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/apps/{key}/run", executeSingleAction).Methods("POST", "OPTIONS")
|
||||
|
||||
// Agent / MCP actions
|
||||
r.HandleFunc("/api/v1/apps/{key}/mcp", runMCPAction).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/mcp", runMCPAction).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/agent", runMCPAction).Methods("POST", "OPTIONS")
|
||||
|
||||
|
||||
//r.HandleFunc("/api/v1/apps/categories/run", shuffle.RunCategoryAction).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/apps/upload", handleAppZipUpload).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/apps/{appId}/activate", activateWorkflowAppDocker).Methods("GET", "OPTIONS")
|
||||
@@ -5532,7 +5946,7 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/workflows/{key}/executions/{key}/rerun", checkUnfinishedExecution).Methods("GET", "POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/{key}/executions/{key}/abort", shuffle.AbortExecution).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/{key}/schedule", scheduleWorkflow).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/download_remote", loadSpecificWorkflows).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/download_remote", shuffle.LoadSpecificWorkflows).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/{key}/run", executeWorkflow).Methods("GET", "POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/{key}/execute", executeWorkflow).Methods("GET", "POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/{key}/schedule/{schedule}", stopSchedule).Methods("DELETE", "OPTIONS")
|
||||
@@ -5706,16 +6120,17 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/dashboards/{key}/widgets", shuffle.HandleNewWidget).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/dashboards/{key}/widgets/{widget_id}", shuffle.HandleGetWidget).Methods("GET", "OPTIONS")
|
||||
|
||||
if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" || strings.ToLower(os.Getenv("DEBUG_MEMORY")) == "true" {
|
||||
log.Printf("[DEBUG] Memory debugging is enabled on /debug/pprof")
|
||||
r.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
r.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
|
||||
r.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
r.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Memory debugging is disabled. To enable, set SHUFFLE_DEBUG_MEMORY or DEBUG_MEMORY to true")
|
||||
}
|
||||
// Need to add auth in pprof
|
||||
// if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" || strings.ToLower(os.Getenv("DEBUG_MEMORY")) == "true" {
|
||||
// log.Printf("[DEBUG] Memory debugging is enabled on /debug/pprof")
|
||||
// r.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
// r.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
|
||||
// r.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
// r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
// r.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
// } else {
|
||||
// log.Printf("[DEBUG] Memory debugging is disabled. To enable, set SHUFFLE_DEBUG_MEMORY or DEBUG_MEMORY to true")
|
||||
// }
|
||||
|
||||
r.Use(shuffle.RequestMiddleware)
|
||||
http.Handle("/", r)
|
||||
|
||||
@@ -14,13 +14,17 @@ import (
|
||||
|
||||
//"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"net/url"
|
||||
|
||||
http2 "github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/h2non/filetype"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
@@ -33,7 +37,7 @@ import (
|
||||
"github.com/go-git/go-billy/v5/memfs"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
http2 "github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
)
|
||||
|
||||
@@ -1261,7 +1265,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
// Check for "wait" query if it's true
|
||||
wait, waitok := request.URL.Query()["wait"]
|
||||
if waitok && wait[0] == "true" {
|
||||
returnBody := shuffle.HandleRetValidation(ctx, workflowExecution, 1)
|
||||
returnBody := shuffle.HandleRetValidation(ctx, workflowExecution, 1, 15)
|
||||
returnBytes, err := json.Marshal(returnBody)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to marshal retStruct in single execution: %s", err)
|
||||
@@ -2008,132 +2012,6 @@ func validateAppInput(resp http.ResponseWriter, request *http.Request) {
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
||||
}
|
||||
|
||||
func loadGithubWorkflows(url, username, password, userId, branch, orgId string) error {
|
||||
fs := memfs.New()
|
||||
|
||||
log.Printf("Starting load of %s with branch %s", url, branch)
|
||||
if strings.Contains(url, "github") || strings.Contains(url, "gitlab") || strings.Contains(url, "bitbucket") {
|
||||
cloneOptions := &git.CloneOptions{
|
||||
URL: url,
|
||||
}
|
||||
|
||||
// FIXME: Better auth.
|
||||
if len(username) > 0 && len(password) > 0 {
|
||||
cloneOptions.Auth = &http2.BasicAuth{
|
||||
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
|
||||
// main is the new master
|
||||
if len(branch) > 0 && branch != "main" && branch != "master" {
|
||||
cloneOptions.ReferenceName = plumbing.ReferenceName(branch)
|
||||
}
|
||||
|
||||
cloneOptions = checkGitProxy(cloneOptions)
|
||||
|
||||
storer := memory.NewStorage()
|
||||
r, err := git.Clone(storer, fs, cloneOptions)
|
||||
if err != nil {
|
||||
log.Printf("[INFO] Failed loading repo %s into memory (github workflows): %s", url, err)
|
||||
return err
|
||||
}
|
||||
|
||||
dir, err := fs.ReadDir("/")
|
||||
if err != nil {
|
||||
log.Printf("FAiled reading folder: %s", err)
|
||||
}
|
||||
_ = r
|
||||
|
||||
log.Printf("Starting workflow folder iteration")
|
||||
iterateWorkflowGithubFolders(fs, dir, "", "", userId, orgId)
|
||||
|
||||
} else if strings.Contains(url, "s3") {
|
||||
//https://docs.aws.amazon.com/sdk-for-go/api/service/s3/
|
||||
|
||||
//sess := session.Must(session.NewSession())
|
||||
//downloader := s3manager.NewDownloader(sess)
|
||||
|
||||
//// Write the contents of S3 Object to the file
|
||||
//storer := memory.NewStorage()
|
||||
//n, err := downloader.Download(storer, &s3.GetObjectInput{
|
||||
// Bucket: aws.String(myBucket),
|
||||
// Key: aws.String(myString),
|
||||
//})
|
||||
//if err != nil {
|
||||
// return fmt.Errorf("failed to download file, %v", err)
|
||||
//}
|
||||
//fmt.Printf("file downloaded, %d bytes\n", n)
|
||||
} else {
|
||||
return errors.New(fmt.Sprintf("URL %s is unsupported when downloading workflows", url))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSpecificWorkflows(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := shuffle.HandleCors(resp, request)
|
||||
if cors {
|
||||
return
|
||||
}
|
||||
|
||||
// Just need to be logged in
|
||||
// FIXME - should have some permissions?
|
||||
user, err := shuffle.HandleApiAuthentication(resp, request)
|
||||
if err != nil {
|
||||
log.Printf("Api authentication failed in load apps: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
if user.Role != "admin" {
|
||||
log.Printf("Wrong user (%s) when downloading from github", user.Username)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Downloading remotely requires admin"}`))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
log.Printf("Error with body read: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
// Field1 & 2 can be a lot of things..
|
||||
type tmpStruct struct {
|
||||
URL string `json:"url"`
|
||||
Field1 string `json:"field_1"`
|
||||
Field2 string `json:"field_2"`
|
||||
Field3 string `json:"field_3"`
|
||||
}
|
||||
//log.Printf("Body: %s", string(body))
|
||||
|
||||
var tmpBody tmpStruct
|
||||
err = json.Unmarshal(body, &tmpBody)
|
||||
if err != nil {
|
||||
log.Printf("Error with unmarshal tmpBody: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
// Field3 = branch
|
||||
err = loadGithubWorkflows(tmpBody.URL, tmpBody.Field1, tmpBody.Field2, user.Id, tmpBody.Field3, user.ActiveOrg.Id)
|
||||
if err != nil {
|
||||
log.Printf("Failed to update workflows: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
||||
}
|
||||
|
||||
func handleSingleAppHotloadRequest(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := shuffle.HandleCors(resp, request)
|
||||
if cors {
|
||||
@@ -2418,116 +2296,6 @@ func iterateOpenApiGithub(fs billy.Filesystem, dir []os.FileInfo, extra string,
|
||||
return nil
|
||||
}
|
||||
|
||||
// Onlyname is used to
|
||||
func iterateWorkflowGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra string, onlyname, userId, orgId string) error {
|
||||
var err error
|
||||
secondsOffset := 0
|
||||
|
||||
// sort file names
|
||||
filenames := []string{}
|
||||
for _, file := range dir {
|
||||
filename := file.Name()
|
||||
filenames = append(filenames, filename)
|
||||
}
|
||||
sort.Strings(filenames)
|
||||
|
||||
// iterate through sorted filenames
|
||||
for _, filename := range filenames {
|
||||
secondsOffset -= 10
|
||||
if len(onlyname) > 0 && filename != onlyname {
|
||||
continue
|
||||
}
|
||||
|
||||
file, err := fs.Stat(filename)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Folder?
|
||||
switch mode := file.Mode(); {
|
||||
case mode.IsDir():
|
||||
tmpExtra := fmt.Sprintf("%s%s/", extra, file.Name())
|
||||
dir, err := fs.ReadDir(tmpExtra)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read dir: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Go routine? Hmm, this can be super quick I guess
|
||||
err = iterateWorkflowGithubFolders(fs, dir, tmpExtra, "", userId, orgId)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
case mode.IsRegular():
|
||||
// Check the file
|
||||
if strings.HasSuffix(filename, ".json") {
|
||||
path := fmt.Sprintf("%s%s", extra, file.Name())
|
||||
fileReader, err := fs.Open(path)
|
||||
if err != nil {
|
||||
log.Printf("Error reading file: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
readFile, err := ioutil.ReadAll(fileReader)
|
||||
if err != nil {
|
||||
log.Printf("Error reading file: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var workflow shuffle.Workflow
|
||||
err = json.Unmarshal(readFile, &workflow)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// rewrite owner to user who imports now
|
||||
if userId != "" {
|
||||
workflow.Owner = userId
|
||||
}
|
||||
|
||||
workflow.ID = uuid.NewV4().String()
|
||||
workflow.OrgId = orgId
|
||||
workflow.ExecutingOrg = shuffle.OrgMini{
|
||||
Id: orgId,
|
||||
}
|
||||
|
||||
workflow.Org = append(workflow.Org, shuffle.OrgMini{
|
||||
Id: orgId,
|
||||
})
|
||||
workflow.IsValid = false
|
||||
workflow.Errors = []string{"Imported, not locally saved. Save before using."}
|
||||
|
||||
/*
|
||||
// Find existing similar ones
|
||||
q = datastore.NewQuery("workflow").Filter("org_id =", user.ActiveOrg.Id).Filter("name", workflow.name)
|
||||
var workflows []Workflow
|
||||
_, err = dbclient.GetAll(ctx, q, &workflows)
|
||||
if err == nil {
|
||||
log.Printf("Failed getting workflows for user %s: %s", user.Username, err)
|
||||
if len(workflows) == 0 {
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte("[]"))
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
log.Printf("Import workflow from file: %s", filename)
|
||||
ctx := context.Background()
|
||||
err = shuffle.SetWorkflow(ctx, workflow, workflow.ID, secondsOffset)
|
||||
if err != nil {
|
||||
log.Printf("Failed setting (download) workflow: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Uploaded workflow %s for user %s and org %s!", filename, userId, orgId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func setNewWorkflowApp(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := shuffle.HandleCors(resp, request)
|
||||
if cors {
|
||||
@@ -2959,7 +2727,7 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) {
|
||||
actionId = workflowExecution.Workflow.Actions[0].ID
|
||||
}
|
||||
|
||||
returnBody := shuffle.HandleRetValidation(ctx, workflowExecution, 1, actionId)
|
||||
returnBody := shuffle.HandleRetValidation(ctx, workflowExecution, 1, 15, actionId)
|
||||
returnBytes, err := json.Marshal(returnBody)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to marshal retStruct in single execution: %s", err)
|
||||
@@ -3506,7 +3274,7 @@ func LoadSpecificApps(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
cloneOptions = checkGitProxy(cloneOptions)
|
||||
cloneOptions = shuffle.CheckGitProxy(cloneOptions)
|
||||
|
||||
storer := memory.NewStorage()
|
||||
r, err := git.Clone(storer, fs, cloneOptions)
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
# Build environment
|
||||
FROM node:23 as builder
|
||||
FROM node:23 AS builder
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
ENV PATH="/usr/src/app/node_modules/.bin:$PATH"
|
||||
|
||||
COPY package.json /usr/src/app/package.json
|
||||
COPY package-lock.json /usr/src/app/package-lock.json
|
||||
|
||||
# Nocache yarn install
|
||||
RUN npm config set fetch-retries 3 # Number of retry attempts (default is 2)
|
||||
RUN npm config set fetch-retry-mintimeout 5000 # Minimum wait time before retrying (in ms, default is 10000)
|
||||
RUN npm config set fetch-retry-maxtimeout 60000 # Maximum wait time before retrying (in ms, default is 60000)
|
||||
RUN npm config set fetch-timeout 60000 # Overall fetch timeout (in ms, default is 300000)
|
||||
RUN npm install --timeout=60000 --legacy-peer-deps
|
||||
RUN npm install --legacy-peer-deps
|
||||
|
||||
# copy only required files to not trigger rebuilding every time
|
||||
COPY ./certs /usr/src/app/certs/
|
||||
@@ -22,9 +22,11 @@ COPY ./public /usr/src/app/public/
|
||||
COPY ./src /usr/src/app/src/
|
||||
COPY ./*.sh /usr/src/app/
|
||||
COPY ./*.json /usr/src/app/
|
||||
COPY ./index.html /usr/src/app/index.html
|
||||
COPY ./vite.config.js /usr/src/app/vite.config.js
|
||||
|
||||
|
||||
RUN npm run build --loglevel verbose 2>&1
|
||||
RUN npm run build
|
||||
|
||||
# Production environment
|
||||
FROM nginx:1.29.3
|
||||
@@ -36,7 +38,7 @@ RUN mkdir -p /usr/share/nginx/html/img
|
||||
|
||||
|
||||
# Localhost certificate challenge: Y#XwrJ#DoZGz2w6x
|
||||
# Cert challenge doesn't matter to be here or not, as ALL production setups should be using their own certificates + reverse proxy: https://shuffler.io/docs/configuration#using-the-nginx-reverse-proxy-for-tls/ssl
|
||||
# Cert challenge doesn't matter to be here or not, as ALL production setups should be using their own certificates + reverse proxy: https://shuffler.io/docs/configuration#using-the-nginx-reverse-proxy-for-tls/ssl
|
||||
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
|
||||
COPY --from=builder /usr/src/app/certs/fullchain.pem /etc/nginx/fullchain.cert.pem
|
||||
COPY --from=builder /usr/src/app/certs/privkey.pem /etc/nginx/privkey.pem
|
||||
@@ -45,7 +47,7 @@ COPY --from=builder /usr/src/app/certs/privkey.pem /etc/nginx/privkey.pem
|
||||
RUN apt-get update && apt-get install -y curl && apt-get clean
|
||||
COPY ./confd/templates/nginx.conf /etc/nginx/nginx.conf.tmpl
|
||||
|
||||
## OLD CONFD THINGS (not compatible with arm)
|
||||
## OLD CONFD THINGS (not compatible with arm)
|
||||
#ENV CONFD_VERSION 0.16.0
|
||||
#RUN curl -sSL https://github.com/kelseyhightower/confd/releases/download/v${CONFD_VERSION}/confd-${CONFD_VERSION}-linux-amd64 -o /usr/local/bin/confd && \
|
||||
# chmod +x /usr/local/bin/confd
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>Shuffle</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -24,7 +24,6 @@
|
||||
"class-transformer": "^0.5.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"cpx": "^1.5.0",
|
||||
"create-react-app": "^5.0.1",
|
||||
"cytoscape": "^3.29.2",
|
||||
"cytoscape-bubblesets": "^4.1.0",
|
||||
"cytoscape-clipboard": "^2.2.1",
|
||||
@@ -76,7 +75,6 @@
|
||||
"react-powerhooks": "^0.0.7",
|
||||
"react-router": "^6.14.1",
|
||||
"react-router-dom": "^6.14.1",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-social-icons": "^5.15.0",
|
||||
"react-toastify": "^9.1.3",
|
||||
"reaviz": "^14.9.7",
|
||||
@@ -90,22 +88,18 @@
|
||||
"shellwords": "^1.0.1",
|
||||
"simplebar": "^4.2.3",
|
||||
"styled-components": "^4.4.1",
|
||||
"terser-webpack-plugin": "^4.2.3",
|
||||
"uuid": "^13.0.0",
|
||||
"yaml": "^1.10.0",
|
||||
"yamljs": "^0.3.0",
|
||||
"zone.js": "^0.15.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint 'src/**/*.{tsx,ts,js,jsx}'",
|
||||
"lint_file": "eslint 'src/views/AngularWorkflow.jsx'"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
@@ -118,15 +112,15 @@
|
||||
"@babel/plugin-proposal-export-default-from": "^7.22.5",
|
||||
"@babel/preset-flow": "^7.22.5",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"babel-loader": "^8.0.5",
|
||||
"babel-plugin-css-modules-transform": "^1.6.2",
|
||||
"babel-plugin-react-css-modules": "^5.2.6",
|
||||
"babel-plugin-transform-imports": "^2.0.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"postcss": "^8.4.38",
|
||||
"promise-window": "^1.2.1",
|
||||
"selenium-webdriver": "^4.29.0",
|
||||
"vite": "^5.4.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.59995 4.7C1.44015 4.7 0.499951 3.7598 0.499951 2.6C0.499951 1.4402 1.44015 0.5 2.59995 0.5C3.75975 0.5 4.69995 1.4402 4.69995 2.6C4.69995 3.7598 3.75975 4.7 2.59995 4.7Z" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.4 14.4998C11.2402 14.4998 10.3 13.5596 10.3 12.3998C10.3 11.24 11.2402 10.2998 12.4 10.2998C13.5598 10.2998 14.5 11.24 14.5 12.3998C14.5 13.5596 13.5598 14.4998 12.4 14.4998Z" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.69995 2.6001H5.52832C6.62175 2.6001 7.5125 3.47821 7.52811 4.57153L7.61179 10.4287C7.62741 11.522 8.51815 12.4001 9.61158 12.4001H10.3" stroke="#1A1A1A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 774 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.3001 4.1C1.30598 4.1 0.500098 3.29411 0.500098 2.3C0.500098 1.30589 1.30598 0.5 2.3001 0.5C3.29421 0.5 4.1001 1.30589 4.1001 2.3C4.1001 3.29411 3.29421 4.1 2.3001 4.1Z" stroke="#F1F1F1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.7002 12.5001C9.70613 12.5001 8.90024 11.6943 8.90024 10.7001C8.90024 9.70603 9.70613 8.90015 10.7002 8.90015C11.6944 8.90015 12.5002 9.70603 12.5002 10.7001C12.5002 11.6943 11.6944 12.5001 10.7002 12.5001Z" stroke="#F1F1F1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.1001 2.2998H4.52847C5.62189 2.2998 6.51264 3.17792 6.52826 4.27124L6.59193 8.72837C6.60755 9.82169 7.4983 10.6998 8.59173 10.6998H8.9001" stroke="#F1F1F1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 805 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.33325 9.99992C3.33325 6.31802 6.31802 3.33325 9.99992 3.33325V3.33325C13.6818 3.33325 16.6666 6.31802 16.6666 9.99992V14.2423C16.6666 14.6365 16.6666 14.8336 16.6389 14.9982C16.4975 15.8389 15.8389 16.4975 14.9982 16.6389C14.8336 16.6666 14.6365 16.6666 14.2423 16.6666H9.99992C6.31802 16.6666 3.33325 13.6818 3.33325 9.99992V9.99992Z" stroke="#1A1A1A"/>
|
||||
<path d="M7.5 10L12.5 10" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 7.5L10 12.5" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 654 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.68579 8.05757C2.68579 5.09082 5.09082 2.68579 8.05757 2.68579V2.68579C11.0243 2.68579 13.4293 5.09082 13.4293 8.05757V11.476C13.4293 11.7936 13.4293 11.9524 13.407 12.085C13.2931 12.7624 12.7624 13.2931 12.085 13.407C11.9524 13.4293 11.7936 13.4293 11.476 13.4293H8.05757C5.09082 13.4293 2.68579 11.0243 2.68579 8.05757V8.05757Z" stroke="#9E9E9E" stroke-width="0.805766"/>
|
||||
<path d="M6.04321 8.05762L10.072 8.05762" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.05762 6.04321L8.05762 10.072" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 752 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.5 5.41675H17.5C16.6463 5.41675 16.2194 5.41675 15.8788 5.54494C15.3398 5.74781 14.9144 6.17322 14.7115 6.71225C14.5833 7.05284 14.5833 7.4797 14.5833 8.33341V12.2501C14.5833 14.1357 14.5833 15.0785 13.9975 15.6643C13.4118 16.2501 12.469 16.2501 10.5833 16.2501H9.41667C7.53105 16.2501 6.58824 16.2501 6.00245 15.6643C5.41667 15.0785 5.41667 14.1357 5.41667 12.2501V8.33341C5.41667 7.47969 5.41667 7.05284 5.28848 6.71225C5.0856 6.17322 4.6602 5.74781 4.12117 5.54494C3.78058 5.41675 3.35372 5.41675 2.5 5.41675Z" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.91659 2.91679C7.91659 2.91679 8.33325 2.08325 9.99992 2.08325C11.6666 2.08325 12.0833 2.91659 12.0833 2.91659" stroke="#1A1A1A" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 859 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.0144 4.3645H14.1009C13.413 4.3645 13.0691 4.3645 12.7946 4.46779C12.3603 4.63126 12.0175 4.97404 11.854 5.40837C11.7507 5.68281 11.7507 6.02676 11.7507 6.71465V9.87057C11.7507 11.3899 11.7507 12.1496 11.2787 12.6216C10.8067 13.0936 10.047 13.0936 8.52768 13.0936H7.58762C6.06825 13.0936 5.30857 13.0936 4.83656 12.6216C4.36456 12.1496 4.36456 11.3899 4.36456 9.87057V6.71465C4.36456 6.02676 4.36456 5.68281 4.26127 5.40837C4.0978 4.97404 3.75502 4.63126 3.32069 4.46779C3.04625 4.3645 2.7023 4.3645 2.0144 4.3645Z" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.37897 2.35035C6.37897 2.35035 6.7147 1.67871 8.05765 1.67871C9.40059 1.67871 9.73633 2.35018 9.73633 2.35018" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 908 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.8335 7.50008L10.0002 11.6667M10.0002 11.6667L14.1668 7.50008M10.0002 11.6667L10.0002 4.16675" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.1665 13.3333L4.1665 14.1666C4.1665 15.0871 4.9127 15.8333 5.83317 15.8333L14.1665 15.8333C15.087 15.8333 15.8332 15.0871 15.8332 14.1666V13.3333" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 499 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.7002 6.04328L8.05755 9.40063M8.05755 9.40063L11.4149 6.04328M8.05755 9.40063L8.05756 3.35739" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.35742 10.7437L3.35742 11.4151C3.35742 12.1568 3.95868 12.7581 4.70037 12.7581L11.4151 12.7581C12.1568 12.7581 12.758 12.1568 12.758 11.4151V10.7437" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 550 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.6665 11.6667V16.6667M16.6665 16.6667H11.6665M16.6665 16.6667L12.4998 12.5001" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.3335 11.6667V16.6667M3.3335 16.6667H8.3335M3.3335 16.6667L7.50016 12.5001" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.6665 8.33325V3.33325M16.6665 3.33325H11.6665M16.6665 3.33325L12.4998 7.49992" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.3335 8.33325V3.33325M3.3335 3.33325H8.3335M3.3335 3.33325L7.50016 7.49992" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.6665 11.6667V16.6667M16.6665 16.6667H11.6665M16.6665 16.6667L12.4998 12.5001" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.3335 11.6667V16.6667M3.3335 16.6667H8.3335M3.3335 16.6667L7.50016 12.5001" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.6665 8.33325V3.33325M16.6665 3.33325H11.6665M16.6665 3.33325L12.4998 7.49992" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.3335 8.33325V3.33325M3.3335 3.33325H8.3335M3.3335 3.33325L7.50016 7.49992" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 3.77051V8.43717H6.66666" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.95222 12.3261C4.45653 13.7575 5.41237 14.9862 6.67573 15.8271C7.93909 16.6681 9.44152 17.0756 10.9567 16.9884C12.4718 16.9012 13.9175 16.324 15.0761 15.3437C16.2346 14.3633 17.0432 13.033 17.3799 11.5532C17.7167 10.0734 17.5634 8.52426 16.9431 7.13915C16.3229 5.75404 15.2693 4.60803 13.9411 3.87378C12.6129 3.13954 11.082 2.85684 9.57919 3.06828C8.07635 3.27972 6.68293 3.97385 5.60889 5.04608L2 8.43718" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.4678 6.01172V10.095L12.4122 11.0673" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 806 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.61157 3.03809V6.79833H5.37181" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.18461 9.93187C3.59096 11.0852 4.36114 12.0753 5.37912 12.7529C6.39709 13.4305 7.6077 13.7589 8.82855 13.6886C10.0494 13.6184 11.2143 13.1532 12.1478 12.3633C13.0814 11.5734 13.7329 10.5015 14.0042 9.30915C14.2755 8.11677 14.152 6.8685 13.6522 5.75243C13.1525 4.63635 12.3035 3.71293 11.2333 3.1213C10.1631 2.52967 8.92956 2.30188 7.71862 2.47225C6.50769 2.64263 5.38492 3.20193 4.51949 4.0659L1.61157 6.79834" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.43481 4.84399V8.1342L10.0016 8.91759" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 888 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="11" height="13" viewBox="0 0 11 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.402832 1.21052C0.402832 0.580157 1.09404 0.193914 1.6309 0.524288L9.76266 5.52845C10.2739 5.84309 10.2739 6.58628 9.76266 6.90092L1.6309 11.9051C1.09404 12.2355 0.402832 11.8492 0.402832 11.2188V1.21052Z" stroke="#1A1A1A" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 410 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.2002 11.4463L6.48223 12.1027L7.46684 9.14885L8.45145 4.55396" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.4668 9.47681L9.43602 10.7896L9.76422 14.3999" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.45147 5.21021L5.16943 6.19483V8.16407" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.7793 2.00342C9.46296 2.00342 10.0174 2.55709 10.0176 3.24072C10.0176 3.92454 9.4631 4.479 8.7793 4.479C8.09568 4.47879 7.54199 3.92441 7.54199 3.24072C7.54221 2.55723 8.09582 2.00363 8.7793 2.00342Z" fill="#1A1A1A" stroke="#1A1A1A" stroke-width="0.805766"/>
|
||||
<path d="M8.45117 5.21021L10.7486 7.50765L12.3896 8.16407" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 879 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="11" height="14" viewBox="0 0 11 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.5 9.8462L3.78203 10.5026L4.76664 7.54875L5.75125 2.95386" stroke="#F1F1F1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.7666 7.87671L6.73582 9.18954L7.06403 12.7998" stroke="#F1F1F1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.75103 3.61011L2.46899 4.59473V6.56397" stroke="#F1F1F1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.07886 0.40332C6.76252 0.40332 7.31692 0.956997 7.31714 1.64062C7.31714 2.32444 6.76266 2.87891 6.07886 2.87891C5.39524 2.87869 4.84155 2.32431 4.84155 1.64062C4.84177 0.957128 5.39538 0.403532 6.07886 0.40332Z" fill="#F1F1F1" stroke="#F1F1F1" stroke-width="0.805766"/>
|
||||
<path d="M5.75098 3.61011L8.0484 5.90756L9.68942 6.56397" stroke="#F1F1F1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 885 B |
|
After Width: | Height: | Size: 6.6 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.50016 12.5L3.3335 8.33333M3.3335 8.33333L7.50016 4.16667M3.3335 8.33333L10.6668 8.33333C13.9805 8.33333 16.6668 11.0196 16.6668 14.3333L16.6668 15" stroke="#1A1A1A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 329 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.04315 10.072L2.68579 6.71465M2.68579 6.71465L6.04315 3.35729M2.68579 6.71465L8.59475 6.71465C11.2648 6.71465 13.4293 8.87917 13.4293 11.5492L13.4293 12.0864" stroke="#9E9E9E" stroke-width="0.805766" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 363 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6666 15.8333H12.9999C14.1045 15.8333 14.9999 14.9378 14.9999 13.8333V12.3948C14.9999 11.4771 15.6245 10.6771 16.5148 10.4545L17.3631 10.2425C17.6156 10.1793 17.6156 9.8205 17.3631 9.75738L16.5148 9.54532C15.6245 9.32273 14.9999 8.52277 14.9999 7.60503V6.16658C14.9999 5.06202 14.1045 4.16658 12.9999 4.16658H11.6666" stroke="#1A1A1A" stroke-linejoin="round"/>
|
||||
<path d="M8.33341 4.16675H7.00008C5.89551 4.16675 5.00008 5.06218 5.00008 6.16675V7.6052C5.00008 8.52293 4.37549 9.3229 3.48515 9.54548L2.63689 9.75755C2.3844 9.82067 2.3844 10.1795 2.63689 10.2426L3.48515 10.4547C4.37549 10.6773 5.00008 11.4772 5.00008 12.395V13.8334C5.00008 14.938 5.89551 15.8334 7.00008 15.8334H8.33341" stroke="#1A1A1A" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 844 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.40056 12.7581H10.4749C11.3649 12.7581 12.0865 12.0365 12.0865 11.1465V9.98747C12.0865 9.24799 12.5897 8.60341 13.3071 8.42406L13.9906 8.25318C14.1941 8.20232 14.1941 7.91319 13.9906 7.86233L13.3071 7.69145C12.5897 7.5121 12.0865 6.86751 12.0865 6.12803V4.96898C12.0865 4.07896 11.3649 3.35745 10.4749 3.35745H9.40056" stroke="#9E9E9E" stroke-width="0.805766" stroke-linejoin="round"/>
|
||||
<path d="M6.71467 3.35742H5.64032C4.75029 3.35742 4.02878 4.07893 4.02878 4.96895V6.12801C4.02878 6.86749 3.52551 7.51207 2.8081 7.69142L2.1246 7.8623C1.92116 7.91316 1.92116 8.20229 2.1246 8.25315L2.8081 8.42403C3.52551 8.60338 4.02878 9.24796 4.02878 9.98744V11.1465C4.02878 12.0365 4.75029 12.758 5.64032 12.758H6.71467" stroke="#9E9E9E" stroke-width="0.805766" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 891 B |
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useContext } from "react";
|
||||
|
||||
import { Link, Route, Routes, BrowserRouter, useNavigate } from "react-router-dom";
|
||||
import { CookiesProvider } from "react-cookie";
|
||||
import { removeCookies, useCookies } from "react-cookie";
|
||||
import { useCookies } from "react-cookie";
|
||||
|
||||
import Workflows from "./views/Workflows.jsx";
|
||||
import GettingStarted from "./views/GettingStarted.jsx";
|
||||
@@ -72,6 +72,9 @@ import AppExplorer from "./views/AppExplorer.jsx";
|
||||
// Production - backend proxy forwarding in nginx
|
||||
var globalUrl = window.location.origin;
|
||||
|
||||
// Change the Shuffle version on UI from here
|
||||
const SHUFFLE_VERSION = "2.2.0"
|
||||
|
||||
// CORS used for testing purposes. Should only happen with specific port and http
|
||||
if (window.location.port === "3000") {
|
||||
globalUrl = "http://localhost:5001";
|
||||
@@ -350,6 +353,7 @@ const App = (message, props) => {
|
||||
{...props}
|
||||
/> */}
|
||||
<Navbar
|
||||
SHUFFLE_VERSION={SHUFFLE_VERSION}
|
||||
notifications={notifications}
|
||||
setNotifications={setNotifications}
|
||||
userdata={userdata}
|
||||
@@ -365,7 +369,7 @@ const App = (message, props) => {
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ position: 'fixed', top: 32, left: 10, zIndex: 100000 }}>
|
||||
<LeftSideBar checkLogin={checkLogin} userdata={userdata} globalUrl={globalUrl} notifications={notifications} />
|
||||
<LeftSideBar SHUFFLE_VERSION={SHUFFLE_VERSION} checkLogin={checkLogin} userdata={userdata} globalUrl={globalUrl} notifications={notifications} />
|
||||
</div>
|
||||
) }
|
||||
|
||||
@@ -514,7 +518,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
@@ -530,7 +533,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
@@ -694,7 +696,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
@@ -711,7 +712,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
@@ -727,7 +727,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
@@ -938,7 +937,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
@@ -955,7 +953,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
@@ -984,7 +981,6 @@ const App = (message, props) => {
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
checkLogin={checkLogin}
|
||||
{...props}
|
||||
@@ -992,6 +988,20 @@ const App = (message, props) => {
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/agent"
|
||||
element={
|
||||
<AgentUI
|
||||
serverside={false}
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/agents"
|
||||
|
||||
@@ -15,7 +15,7 @@ const AnalyticsTab = (props) => {
|
||||
// const [checked, setChecked] = useState(false);
|
||||
// const [selectedOption, setSelectedOption] = useState('all');
|
||||
// const [expand, setExpand] = useState(false)
|
||||
// const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
|
||||
// const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
||||
|
||||
// const handleOptionChange = (option) => {
|
||||
// setSelectedOption(option);
|
||||
|
||||
@@ -1050,7 +1050,7 @@ const AppAuthTab = memo((props) => {
|
||||
|
||||
<ListItem style={{ width: "100%", paddingTop: 10, paddingBottom: 10, paddingRight: 10, borderBottom: theme.palette.defaultBorder, display: 'table-row'}}>
|
||||
|
||||
{["Valid", "Label", "App Name", "Workflows", "Fields", "Edited", "Actions", "Distribution"].map((header, index) => (
|
||||
{["Logo", "Valid", "Label", "App Name", "Fields", "Edited", "Actions", "Distribute"].map((header, index) => (
|
||||
<ListItemText
|
||||
key={index}
|
||||
primary={header}
|
||||
@@ -1145,9 +1145,33 @@ const AppAuthTab = memo((props) => {
|
||||
}
|
||||
return (
|
||||
<ListItem key={index} style={{ backgroundColor: bgColor, borderBottomLeftRadius: authentication?.length - 1 === index ? 8 : 0, borderBottomRightRadius: authentication?.length - 1 === index ? 8 : 0, display: 'table-row' }}>
|
||||
|
||||
<ListItemText
|
||||
primary= {(
|
||||
<Tooltip title={data.validation !== null && data.validation !== undefined && data.validation.valid === true ? "Valid. Click to explore." : "Configuration failed. Click to learn why"} placement="top">
|
||||
<Tooltip title={`Auth for ${data.app.name}. Click to explore.`} placement="top">
|
||||
<IconButton>
|
||||
<img
|
||||
style={{height: 30, width: 30, borderRadius: theme.palette?.borderRadius }}
|
||||
src={data.app.large_image ? data.app.large_image : '/images/no_image.png'}
|
||||
/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
style={{ display: "table-cell", verticalAlign: 'middle', minWidth: 60 }}
|
||||
primaryTypographyProps={{
|
||||
style: {
|
||||
padding: "8px 8px 8px 15px",
|
||||
}
|
||||
}}
|
||||
onClick={() => {
|
||||
const url = `/apps/${data.app.id}`
|
||||
window.open(url, "_blank")
|
||||
}}
|
||||
/>
|
||||
|
||||
<ListItemText
|
||||
primary= {(
|
||||
<Tooltip title={data.validation !== null && data.validation !== undefined && data.validation.valid === true ? `Last Valid ${new Date(data?.validation?.last_valid).toISOString()}. Click to explore.` : "Configuration failed. Click to learn why"} placement="top">
|
||||
<IconButton>
|
||||
{validIcon}
|
||||
</IconButton>
|
||||
@@ -1202,22 +1226,7 @@ const AppAuthTab = memo((props) => {
|
||||
}}
|
||||
style={{ marginLeft: 10, display: "table-cell", textAlign: 'center', verticalAlign: 'middle', padding: 8 }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={
|
||||
data.workflow_count === null ? 0 : data.workflow_count
|
||||
}
|
||||
primaryTypographyProps={{
|
||||
style: {
|
||||
padding: 8
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
display: "table-cell",
|
||||
textAlign: "center",
|
||||
overflow: "hidden",
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
/>
|
||||
|
||||
<ListItemText
|
||||
primary={
|
||||
data.fields === null || data.fields === undefined
|
||||
@@ -1239,6 +1248,7 @@ const AppAuthTab = memo((props) => {
|
||||
verticalAlign: 'middle'
|
||||
}}
|
||||
/>
|
||||
|
||||
<ListItemText
|
||||
style={{
|
||||
overflow: "hidden",
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
Paper,
|
||||
TextField,
|
||||
Collapse,
|
||||
iconButton,
|
||||
IconButton,
|
||||
Avatar,
|
||||
ButtonBase,
|
||||
InputAdornment,
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
|
||||
@@ -64,6 +64,7 @@ const AppSelection = props => {
|
||||
document.title = "Choose your apps"
|
||||
const ref = useRef()
|
||||
let navigate = useNavigate();
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
import aa from 'search-insights'
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e")
|
||||
const Appsearch = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp, inputHeight, } = props
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp, inputHeight, apps, } = props
|
||||
const { themeMode } = useContext(Context)
|
||||
const theme = getTheme(themeMode)
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
@@ -75,7 +75,7 @@ const Appsearch = props => {
|
||||
color="primary"
|
||||
defaultValue={defaultSearch}
|
||||
// placeholder={`Find ${defaultSearch} Apps...`}
|
||||
placeholder= {defaultSearch ? `${defaultSearch}` : "Search Cases "}
|
||||
placeholder= {defaultSearch ? `${defaultSearch}` : "Search Apps "}
|
||||
id="shuffle_workflow_search_field"
|
||||
onChange={(event) => {
|
||||
refine(event.currentTarget.value)
|
||||
@@ -92,6 +92,30 @@ const Appsearch = props => {
|
||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1)
|
||||
var counted = 0
|
||||
|
||||
if (hits?.length < 20 && apps !== undefined && apps !== null && apps?.length > 0) {
|
||||
// Find the textbox html
|
||||
const findId = "shuffle_workflow_search_field"
|
||||
const searchField = document.getElementById(findId)
|
||||
const searchValue = searchField === null || searchField === undefined ? "" : searchField?.value
|
||||
if (searchValue?.length > 0) {
|
||||
const parsedsearch = searchValue.toLowerCase().replaceAll(" ", "")
|
||||
for (var appkey in apps) {
|
||||
const app = apps[appkey]
|
||||
const parsedAppname = app?.name?.toLowerCase().replaceAll(" ", "")
|
||||
if (parsedAppname.includes(parsedsearch)) {
|
||||
hits.push({
|
||||
objectID: app.id,
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
image_url: app.large_image,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("No value in textfield: ", searchValue)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={0} style={{border: "1px solid rgba(255,255,255,0.2)", maxHeight: parsedInputHeight, minHeight: parsedInputHeight, overflowY: "auto", overflowX: "hidden", }}>
|
||||
{hits.map((data, index) => {
|
||||
|
||||
@@ -97,6 +97,12 @@ const AuthenticationModal = (props) => {
|
||||
}
|
||||
|
||||
if (authenticationModalOpen === false) {
|
||||
// Add 5px for every character over 12
|
||||
var addednumber = selectedAppData?.name?.length > 12 ? (selectedAppData?.name?.length - 12) * 5 : 0
|
||||
if (addednumber > 50) {
|
||||
addednumber = 50
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
fullWidth
|
||||
@@ -111,8 +117,8 @@ const AuthenticationModal = (props) => {
|
||||
backgroundColor: "#ffffff",
|
||||
color: "#2f2f2f",
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
minWidth: 275,
|
||||
maxWidth: 275,
|
||||
minWidth: 275+addednumber,
|
||||
maxWidth: 275+addednumber,
|
||||
maxHeight: 50,
|
||||
overflow: "hidden",
|
||||
border: `1px solid ${theme.palette.inputColor}`,
|
||||
@@ -128,10 +134,10 @@ const AuthenticationModal = (props) => {
|
||||
<span style={{display: "flex"}}>
|
||||
<img
|
||||
alt={selectedAppData?.name}
|
||||
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
|
||||
style={{ margin: "4px 4px 4px 0px", minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
|
||||
src={selectedAppData?.large_image}
|
||||
/>
|
||||
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 8, color: "#2f2f2f",}} variant="body1">
|
||||
<Typography style={{ margin: 0, marginLeft: 5, marginTop: 8, color: "#2f2f2f",}} variant="body1">
|
||||
Authenticate {selectedAppData?.name?.replaceAll("_", " ", -1)}
|
||||
</Typography>
|
||||
</span>
|
||||
|
||||
@@ -4,7 +4,7 @@ import theme from '../theme.jsx';
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
import {
|
||||
Divider,
|
||||
MenuItem,
|
||||
Button,
|
||||
@@ -12,8 +12,7 @@ import {
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Textfield,
|
||||
TextField,
|
||||
TextField,
|
||||
Typography,
|
||||
Select,
|
||||
IconButton,
|
||||
|
||||
@@ -50,6 +50,15 @@ import {
|
||||
Search as SearchIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
Cancel as CancelIcon,
|
||||
Shield as ShieldIcon,
|
||||
Cancel as XCircleIcon,
|
||||
FlashOn as ZapIcon,
|
||||
People as UsersIcon,
|
||||
FmdGoodOutlined as FmdGoodOutlinedIcon,
|
||||
Palette as PaletteIcon,
|
||||
Info as InfoIcon,
|
||||
Email as MailIcon,
|
||||
ArrowForward as ArrowRightIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
//import { useAlert
|
||||
@@ -70,51 +79,301 @@ const ProductionStatus = ({ selectedOrganization, userdata, isCloud, theme }) =>
|
||||
} else {
|
||||
isProdStatusOn = false;
|
||||
}
|
||||
const rows = [
|
||||
{ label: 'Licensed', ok: isProdStatusOn },
|
||||
{ label: 'Multi-Tenant', ok: isProdStatusOn },
|
||||
{ label: 'High Availability', ok: isProdStatusOn },
|
||||
{ label: 'Robust Infrastructure', ok: isProdStatusOn },
|
||||
|
||||
const themeMode = theme.palette.mode;
|
||||
const workflowActive = selectedOrganization?.sync_features?.workflow_executions?.active;
|
||||
const multiTenantActive = selectedOrganization?.sync_features?.multi_tenant?.active;
|
||||
const multiEnvActive = selectedOrganization?.sync_features?.multi_env?.active;
|
||||
const brandingActive = selectedOrganization?.sync_features?.branding?.active;
|
||||
const colors = {
|
||||
textPrimary: theme.palette.text.primary,
|
||||
textSecondary: theme.palette.text.secondary,
|
||||
textMuted: themeMode === "dark" ? '#6e7681' : '#9ca3af',
|
||||
border: themeMode === "dark" ? '#30363d' : '#e1e4e8',
|
||||
divider: themeMode === "dark" ? '#21262d' : '#e5e7eb',
|
||||
success: themeMode === "dark" ? '#10b981' : '#059669',
|
||||
successBg: themeMode === "dark" ? 'rgba(16, 185, 129, 0.12)' : 'rgba(5, 150, 105, 0.08)',
|
||||
warning: '#f59e0b',
|
||||
warningBg: themeMode === "dark" ? 'rgba(245, 158, 11, 0.12)' : 'rgba(245, 158, 11, 0.08)',
|
||||
disabled: themeMode === "dark" ? '#6e7681' : '#d1d5db',
|
||||
disabledBg: themeMode === "dark" ? 'rgba(110, 118, 129, 0.1)' : 'rgba(156, 163, 175, 0.08)',
|
||||
accent: '#f85a3e',
|
||||
cardBg: theme.palette.surfaceColor,
|
||||
};
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: ZapIcon,
|
||||
label: 'Workflow Executions',
|
||||
licensed: `${selectedOrganization?.sync_features?.workflow_executions?.limit}/month limit`,
|
||||
unlicensed: '10,000/month limit',
|
||||
isActive: workflowActive,
|
||||
},
|
||||
{
|
||||
icon: UsersIcon,
|
||||
label: 'Multi-Tenant',
|
||||
licensed: `${selectedOrganization?.sync_features?.multi_tenant?.limit} tenants`,
|
||||
unlicensed: '3 tenants maximum',
|
||||
isActive: multiTenantActive,
|
||||
},
|
||||
{
|
||||
icon: FmdGoodOutlinedIcon,
|
||||
label: 'Runtime Locations',
|
||||
licensed: `${selectedOrganization?.sync_features?.multi_env?.limit} Runtime Locations`,
|
||||
unlicensed: '1 Runtime Location only',
|
||||
isActive: multiEnvActive,
|
||||
},
|
||||
{
|
||||
icon: PaletteIcon,
|
||||
label: 'Custom Branding',
|
||||
licensed: `${brandingActive ? "Full branding control" : "Branding not available"}`,
|
||||
unlicensed: 'Branding not available',
|
||||
isActive: brandingActive,
|
||||
},
|
||||
{
|
||||
icon: ShieldIcon,
|
||||
label: 'High Availability',
|
||||
licensed: 'Enterprise SLA guarantee',
|
||||
unlicensed: 'Standard availability',
|
||||
isActive: isProdStatusOn, // High availability is tied to license status
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', maxWidth: 800, padding: '0px 24px 24px 0px', height: 445 , display: 'flex', flexDirection: 'column', alignItems: 'flex-start'}}>
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, marginBottom: 8 }}>
|
||||
<Typography variant="h5" style={{ fontWeight: 600, fontFamily: theme.typography.fontFamily }}>Production Status</Typography>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 14px', borderRadius: 16, background: isProdStatusOn ? 'rgba(43,192,126,0.1)' : 'rgba(253,76,98,0.1)' }}>
|
||||
<span style={{ width: 8, height: 8, borderRadius: 999, background: isProdStatusOn ? '#2BC07E' : '#FD4C62' }} />
|
||||
<Typography variant="caption" style={{ color: isProdStatusOn ? '#2BC07E' : '#FD4C62', fontWeight: 400, fontFamily: theme.typography.fontFamily }}>{isProdStatusOn ? "ON" : "OFF"}</Typography>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: 800,
|
||||
padding: "0px 24px 24px 0px",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 8 }}>
|
||||
<Typography variant="h5" style={{ fontWeight: 600, margin: 0 }}>
|
||||
License Status
|
||||
</Typography>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
padding: '8px 16px',
|
||||
borderRadius: 20,
|
||||
background: isProdStatusOn ? colors.successBg : colors.warningBg,
|
||||
border: `2px solid ${isProdStatusOn ? colors.success : colors.warning}`,
|
||||
boxShadow: isProdStatusOn
|
||||
? `0 2px 8px ${colors.success}30`
|
||||
: `0 2px 8px ${colors.warning}30`,
|
||||
height: 12,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
background: isProdStatusOn ? colors.success : colors.warning,
|
||||
boxShadow: `0 0 10px ${isProdStatusOn ? colors.success : colors.warning}`,
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 14,
|
||||
fontWeight: 700,
|
||||
color: isProdStatusOn ? colors.success : colors.warning,
|
||||
letterSpacing: '0.5px',
|
||||
}}
|
||||
>
|
||||
{isProdStatusOn ? 'Licensed' : 'Unlicensed'}
|
||||
</span>
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginBottom: 18, fontFamily: theme.typography.fontFamily }}>
|
||||
Monitor your production status to stay informed about available features.
|
||||
</Typography>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
{rows.map((row) => (
|
||||
<div key={row.label} style={{ display: 'flex', alignItems: 'center', gap: 12, fontFamily: theme.typography.fontFamily }}>
|
||||
{row.ok ? (
|
||||
<CheckCircleIcon style={{ color: '#2BC07E' }} />
|
||||
) : (
|
||||
<CancelIcon style={{ color: '#FD4C62' }} />
|
||||
)}
|
||||
<Typography variant="body1" style={{ fontWeight: 400, fontFamily: theme.typography.fontFamily }}>{row.label}</Typography>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Divider style={{
|
||||
width: '100%',
|
||||
marginTop: 32,
|
||||
marginBottom: 16,
|
||||
borderColor: theme.palette.defaultBorder,
|
||||
|
||||
}} />
|
||||
<Typography variant="body1" color="textPrimary" style={{ marginTop: 24, fontFamily: theme.typography.fontFamily }}>
|
||||
Shuffle Enterprise is designed for organizations that require scalability, high availability, dedicated support and more to run mission-critical workflows in production environments.
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary" style={{ marginTop: 24, fontFamily: theme.typography.fontFamily }}>
|
||||
More about upgrading below. If you want to know more, please contact <a href="mailto:support@shuffler.io?subject=Tell%20me%20about%20Production%20readiness" style={{ color: '#f85a3e', textDecoration: 'none' }}>support@shuffler.io</a> directly.
|
||||
</Typography>
|
||||
{/* Subtitle */}
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
style={{
|
||||
margin: '0 0 24px 0',
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
{isProdStatusOn
|
||||
? 'Your organization has full access to all enterprise features and capabilities.'
|
||||
: 'View your current limits and available features. Upgrade to unlock enterprise capabilities.'}
|
||||
</Typography>
|
||||
|
||||
{/* Features Grid */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 12,
|
||||
marginBottom: 32,
|
||||
}}
|
||||
>
|
||||
{features
|
||||
.sort((a, b) => {
|
||||
const aActive = isProdStatusOn && a.isActive;
|
||||
const bActive = isProdStatusOn && b.isActive;
|
||||
return bActive - aActive;
|
||||
})
|
||||
.map((feature, index) => {
|
||||
const Icon = feature.icon;
|
||||
const isAvailable = isProdStatusOn && feature.isActive;
|
||||
const statusColor = isAvailable ? colors.success : colors.disabled;
|
||||
const bgColor = isAvailable ? themeMode === "dark" ? "#212121" : "#ffffff" : colors.disabledBg;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 14,
|
||||
padding: '14px 16px',
|
||||
borderRadius: 10,
|
||||
background: bgColor,
|
||||
border: `1px solid ${isAvailable ? colors.success + '40' : colors.border}`,
|
||||
transition: 'all 0.2s',
|
||||
}}
|
||||
>
|
||||
{/* Icon */}
|
||||
<div
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 8,
|
||||
background: isAvailable ? colors.success + '20' : colors.disabledBg,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Icon style={{ color: statusColor, fontSize: 20 }} />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontWeight: 600,
|
||||
color: colors.textPrimary,
|
||||
marginBottom: 4,
|
||||
}}
|
||||
>
|
||||
{feature.label}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: colors.textSecondary,
|
||||
lineHeight: 1.4,
|
||||
}}
|
||||
>
|
||||
{isProdStatusOn ? feature.licensed : feature.unlicensed}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isAvailable ? (
|
||||
<CheckCircleIcon
|
||||
style={{ color: colors.success, fontSize: 20, flexShrink: 0 }}
|
||||
/>
|
||||
) : (
|
||||
<XCircleIcon
|
||||
style={{ color: colors.disabled, fontSize: 20, flexShrink: 0 }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 1,
|
||||
background: colors.divider,
|
||||
margin: '8px 0 24px 0',
|
||||
}}
|
||||
/>
|
||||
|
||||
{!isProdStatusOn && (
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
borderRadius: 12,
|
||||
background: themeMode == "dark" ? "#212121" : "#ffffff",
|
||||
border: `1px solid ${colors.border}`,
|
||||
marginBottom: 20,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 12, marginBottom: 12 }}>
|
||||
<InfoIcon style={{ color: colors.accent, flexShrink: 0, marginTop: 2, fontSize: 20 }} />
|
||||
<div>
|
||||
<Typography variant="h6" style={{ fontWeight: 600, margin: '0 0 8px 0' }}>
|
||||
About Shuffle Enterprise
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
style={{
|
||||
margin: 0,
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
Shuffle Enterprise is designed for organizations that require scalability, high
|
||||
availability, dedicated support, and robust infrastructure to run mission-critical
|
||||
workflows in production environments. <a href="https://shuffler.io/articles/Shuffle_Open_Source" target="_blank" rel="noreferrer" style={{color: "#FF8544" }}>learn more</a>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>)}
|
||||
|
||||
{/* CTA Section */}
|
||||
{!isProdStatusOn && (
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
borderRadius: 12,
|
||||
background: `linear-gradient(135deg, #FF854415 0%, #FF854408 100%)`,
|
||||
border: `1px solid #FF854440`,
|
||||
marginBottom: 24,
|
||||
}}
|
||||
>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Typography variant="h6" style={{ fontWeight: 600, margin: '0 0 8px 0' }}>
|
||||
Ready to upgrade?
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
style={{
|
||||
margin: 0,
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
Contact our team to learn more about enterprise features and pricing.
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
href="https://shuffler.io/contact?category=talk_to_sales"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<MailIcon sx={{ fontSize: 16}} />}
|
||||
endIcon={<ArrowRightIcon sx={{ fontSize: 16 }} />}
|
||||
>
|
||||
Contact Sales
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2143,14 +2402,14 @@ const Billing = memo((props) => {
|
||||
|
||||
return (
|
||||
<Wrapper clickedFromOrgTab={clickedFromOrgTab}>
|
||||
<div style={{ width: "100%", padding: 24, }}>
|
||||
<div style={{ width: "100%", padding: 24, paddingTop: 0}}>
|
||||
<div style={{ width: "100%", maxWidth: 800, }}>
|
||||
|
||||
{isCloud ? null : <ProductionStatus selectedOrganization={selectedOrganization} userdata={userdata} isCloud={isCloud} theme={theme} />}
|
||||
|
||||
{addDealModal}
|
||||
{clickedFromOrgTab ?
|
||||
<Typography variant="h5" style={{fontSize: 24, fontWeight: 500, marginBottom: 8, marginTop: 24, }}>Billing & Licensing</Typography>
|
||||
<Typography variant="h5" style={{fontSize: 24, fontWeight: 500, marginBottom: 8, }}>Billing & Licensing</Typography>
|
||||
:
|
||||
<Typography variant="h4" style={{ marginTop: 20, marginBottom: 10 }}>
|
||||
Billing & Licensing
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState, useEffect, useContext, useCallback } from 'react';
|
||||
import {getTheme} from '../theme.jsx';
|
||||
import classNames from "classnames";
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
|
||||
import { DataGrid, GridValueGetterParams } from '@mui/x-data-grid'
|
||||
import { DataGrid } from '@mui/x-data-grid'
|
||||
import { toast } from "react-toastify"
|
||||
|
||||
import {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { toast } from 'react-toastify';
|
||||
|
||||
import ReactJson from "react-json-view-ssr";
|
||||
import { validateJson, handleReactJsonClipboard, GetIconInfo, } from "../views/Workflows2.jsx";
|
||||
import { red } from "../views/AngularWorkflow.jsx";
|
||||
import { green, red } from "../views/AngularWorkflow.jsx";
|
||||
import CollectIngestModal from "../components/CollectIngestModal.jsx";
|
||||
import CorrelationGraph from "../components/CorrelationGraph.jsx";
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
@@ -55,7 +55,6 @@ import {
|
||||
OpenInNew as OpenInNewIcon,
|
||||
CloudDownload as CloudDownloadIcon,
|
||||
Description as DescriptionIcon,
|
||||
Polymer as PolymerIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
Close as CloseIcon,
|
||||
Apps as AppsIcon,
|
||||
@@ -63,7 +62,6 @@ import {
|
||||
Cached as CachedIcon,
|
||||
AccessibilityNew as AccessibilityNewIcon,
|
||||
Lock as LockIcon,
|
||||
Eco as EcoIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
Cloud as CloudIcon,
|
||||
Business as BusinessIcon,
|
||||
@@ -71,6 +69,7 @@ import {
|
||||
VisibilityOff as VisibilityOffIcon,
|
||||
Clear as ClearIcon,
|
||||
Add as AddIcon,
|
||||
Remove as RemoveIcon,
|
||||
Rocket as RocketIcon,
|
||||
Webhook as WebhookIcon,
|
||||
Air as AirIcon,
|
||||
@@ -81,6 +80,8 @@ import {
|
||||
FilterAlt as FilterAltIcon,
|
||||
CompareArrows as CompareArrowsIcon,
|
||||
Hub as HubIcon,
|
||||
Key as KeyIcon,
|
||||
FlashOn as FlashOnIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
|
||||
@@ -169,6 +170,18 @@ const CacheView = memo((props) => {
|
||||
|
||||
var to_be_copied = "";
|
||||
const defaultAutomation = [
|
||||
{
|
||||
"name": "Security Rules",
|
||||
"description": "Describes security rules that are validated BEFORE an update occurs. This is in order for bad writes to be avoided. Control: allow, deny, merge, overwrite. Logic: if, or, and. Functions: same_shape, is_superset, has_deleted_field",
|
||||
"options": [{
|
||||
"key": "rule",
|
||||
"value": "",
|
||||
}],
|
||||
"icon": <KeyIcon />,
|
||||
"beta": true,
|
||||
"disabled": false,
|
||||
"enabled": false,
|
||||
},
|
||||
{
|
||||
"name": "Run workflow",
|
||||
"description": "Runs one or more workflows with the updated value as runtime argument",
|
||||
@@ -206,12 +219,14 @@ const CacheView = memo((props) => {
|
||||
|
||||
{
|
||||
"name": "Run AI Agent",
|
||||
"description": "Runs an AI Agent to process the updated value. Uses built-in ShuffleAI configs. Learn more: https://shuffler.io/docs/AI",
|
||||
"description": "Runs an AI Agent to process the updated value. Controllable based on your 'action' input, where the full updated value is added on the end. All actions run in order, meaning it needs to finish the previous one before the next is ran. Uses built-in ShuffleAI configs. Learn more: https://shuffler.io/docs/AI",
|
||||
"type": "singul",
|
||||
"options": [{
|
||||
"key": "",
|
||||
"key": "action-1",
|
||||
"description": "Describe the action to perform on this item",
|
||||
"value": "",
|
||||
}],
|
||||
"beta": true,
|
||||
"icon": <SmartToyIcon />,
|
||||
"disabled": false,
|
||||
},
|
||||
@@ -224,22 +239,10 @@ const CacheView = memo((props) => {
|
||||
"key": "",
|
||||
"value": "",
|
||||
}],
|
||||
"beta": true,
|
||||
"icon": "/images/logos/singul.svg",
|
||||
"disabled": false,
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Send message",
|
||||
"description": "",
|
||||
"type": "singul",
|
||||
"options": [{
|
||||
"key": "app",
|
||||
"value": "",
|
||||
}],
|
||||
"icon": <SendIcon />,
|
||||
"disabled": true,
|
||||
"enabled": false,
|
||||
},
|
||||
]
|
||||
|
||||
const [categoryAutomations, setCategoryAutomations] = useState(defaultAutomation)
|
||||
@@ -323,8 +326,21 @@ const CacheView = memo((props) => {
|
||||
|
||||
var chosenCategory = selectedCategory
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const categoryParam = urlParams.get("category")
|
||||
var categoryParam = urlParams.get("category")
|
||||
if (categoryParam && categoryParam !== undefined && categoryParam !== "default" && categoryParam !== "") {
|
||||
|
||||
// In order to make linking weird urls from workflow page work.
|
||||
if (urlParams.get("src") == "workflow") {
|
||||
if (categoryParam?.toLowerCase().startsWith("list")) {
|
||||
const newParam = categoryParam.substring(5).replaceAll("%20", "_")
|
||||
|
||||
// Replace the url param. There may be more params.
|
||||
urlParams.set("category", newParam)
|
||||
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams.toString()}`)
|
||||
categoryParam = newParam
|
||||
}
|
||||
}
|
||||
|
||||
chosenCategory = categoryParam
|
||||
setSelectedCategory(categoryParam)
|
||||
}
|
||||
@@ -493,6 +509,8 @@ const CacheView = memo((props) => {
|
||||
//if (responseJson.category_config.automations[key].icon === undefined || responseJson.category_config.automations[key].icon === null || responseJson.category_config.automations[key].icon === "") {
|
||||
const foundItem = defaultAutomation.find((automation) => automation.name === responseJson.category_config.automations[key].name)
|
||||
if (foundItem) {
|
||||
responseJson.category_config.automations[key].description = foundItem.description
|
||||
responseJson.category_config.automations[key].beta = foundItem.beta
|
||||
responseJson.category_config.automations[key].disabled = foundItem.disabled
|
||||
responseJson.category_config.automations[key].icon = foundItem.icon
|
||||
responseJson.category_config.automations[key].type = foundItem?.type
|
||||
@@ -572,6 +590,7 @@ const CacheView = memo((props) => {
|
||||
key: dataValue.key,
|
||||
value: value,
|
||||
category: selectedCategory,
|
||||
tags: dataValue?.tags || [],
|
||||
}
|
||||
|
||||
if (dataValue?.category !== undefined && dataValue?.category !== "" && dataValue?.category !== "default") {
|
||||
@@ -588,7 +607,9 @@ const CacheView = memo((props) => {
|
||||
|
||||
setCacheInput([entry]);
|
||||
|
||||
fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, {
|
||||
//entry = [entry]
|
||||
const parsedUrl = `${globalUrl}/api/v2/datastore`
|
||||
fetch(parsedUrl, {
|
||||
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -810,6 +831,37 @@ const CacheView = memo((props) => {
|
||||
Category: {dataValue.category}
|
||||
</Typography>
|
||||
: null}
|
||||
{dataValue?.tags !== undefined && dataValue?.tags !== null && dataValue?.tags?.length > 0 ?
|
||||
<div style={{display: "flex", marginTop: 12, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginRight: 10, marginTop: 4, }}>
|
||||
Tags
|
||||
</Typography>
|
||||
|
||||
{dataValue.tags?.map((tag, index) => {
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
label={tag}
|
||||
style={{ marginRight: 5, }}
|
||||
onDelete={() => {
|
||||
var newTags = dataValue.tags.filter((t) => t !== tag)
|
||||
if (newTags.length === 0) {
|
||||
newTags = ["none"]
|
||||
} else if (newTags.length > 1) {
|
||||
newTags = newTags.filter((t) => t !== "none")
|
||||
}
|
||||
|
||||
setDataValue({
|
||||
...dataValue,
|
||||
tags: newTags,
|
||||
})
|
||||
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
: null}
|
||||
|
||||
</div>
|
||||
: null}
|
||||
@@ -1169,6 +1221,19 @@ const CacheView = memo((props) => {
|
||||
}}>
|
||||
{automation.name}
|
||||
</Typography>
|
||||
{automation?.beta !== true ? null :
|
||||
<Chip
|
||||
label="Beta"
|
||||
size="small"
|
||||
style={{
|
||||
marginLeft: 10,
|
||||
height: 20,
|
||||
fontSize: 12,
|
||||
border: `1px solid ${green}`,
|
||||
color: "rgba(255,255,255,0.7)",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
{automation?.disabled !== true ?
|
||||
@@ -1195,8 +1260,9 @@ const CacheView = memo((props) => {
|
||||
: null}
|
||||
</div>
|
||||
|
||||
{showOptions && (
|
||||
updatedAutomation.options.map((option, optionIndex) => {
|
||||
{showOptions &&
|
||||
<div style={{ marginTop: 20, marginBottom: 20, }}>
|
||||
{updatedAutomation.options.map((option, optionIndex) => {
|
||||
if (option?.key === "datastore_categories") {
|
||||
if (datastoreCategories === undefined || datastoreCategories === null || datastoreCategories.length <= 1) {
|
||||
return (
|
||||
@@ -1475,6 +1541,87 @@ const CacheView = memo((props) => {
|
||||
}}
|
||||
/>
|
||||
)
|
||||
} else if (updatedAutomation?.name === "Run AI Agent") {
|
||||
return (
|
||||
<ButtonGroup style={{display: "flex", }}>
|
||||
<TextField
|
||||
key={optionIndex}
|
||||
fullWidth
|
||||
disabled={option?.disabled === true}
|
||||
style={theme.palette.textFieldStyle}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
style: {
|
||||
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
|
||||
color: theme.palette.textFieldStyle.color,
|
||||
height: 40,
|
||||
},
|
||||
}}
|
||||
label={option.key}
|
||||
defaultValue={option.value}
|
||||
onBlur={(e) => {
|
||||
if (e.target.value === "") {
|
||||
updatedAutomation.enabled = false
|
||||
} else {
|
||||
updatedAutomation.enabled = true
|
||||
}
|
||||
|
||||
updatedAutomation.options[optionIndex].value = e.target.value;
|
||||
setUpdatedAutomation(updatedAutomation)
|
||||
setUpdated(true)
|
||||
}}
|
||||
/>
|
||||
|
||||
{optionIndex === updatedAutomation.options.length - 1 ?
|
||||
<Button color="secondary" variant="outlined" style={{
|
||||
height: 40,
|
||||
maxWidth: 40,
|
||||
}}
|
||||
disabled={option?.value?.length < 20}
|
||||
onClick={() => {
|
||||
|
||||
var newindex = optionIndex + 2
|
||||
if (updatedAutomation.options.length > 0) {
|
||||
const lastOption = updatedAutomation.options[updatedAutomation.options.length - 1]
|
||||
const lastIndex = parseInt(lastOption.key.split("-")[1])
|
||||
if (!isNaN(lastIndex)) {
|
||||
newindex = lastIndex + 1
|
||||
}
|
||||
}
|
||||
|
||||
updatedAutomation.options.push({
|
||||
"key": `action-${newindex}`,
|
||||
"description": "Describe the action to perform on this item",
|
||||
"value": "",
|
||||
})
|
||||
|
||||
setUpdatedAutomation(updatedAutomation)
|
||||
setUpdate(Math.random())
|
||||
}}>
|
||||
<AddIcon/>
|
||||
</Button>
|
||||
:
|
||||
<Tooltip title={option?.disabled !== true ? "Disable action (won't run until re-enabled)" : "Re-Enable action (runs on all new edits)"} placement="top">
|
||||
<Button color="secondary" variant="outlined" style={{
|
||||
height: 40,
|
||||
maxWidth: 40,
|
||||
}} onClick={() => {
|
||||
if (option?.disabled === true) {
|
||||
updatedAutomation.options[optionIndex].disabled = false
|
||||
} else {
|
||||
updatedAutomation.options[optionIndex].disabled = true
|
||||
}
|
||||
|
||||
setUpdatedAutomation(updatedAutomation)
|
||||
setUpdate(Math.random())
|
||||
}}>
|
||||
{option?.disabled !== true ? <RemoveIcon /> : <FlashOnIcon />}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -1507,8 +1654,9 @@ const CacheView = memo((props) => {
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
)}
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
@@ -1601,8 +1749,10 @@ const CacheView = memo((props) => {
|
||||
backgroundColor: theme.palette.platformColor,
|
||||
border: theme.palette.defaultBorder,
|
||||
padding: 5,
|
||||
maxWidth: 500,
|
||||
minWidth: 500,
|
||||
maxHeight: 500,
|
||||
overflowX: "auto",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
collapsed={true}
|
||||
@@ -1784,6 +1934,7 @@ const CacheView = memo((props) => {
|
||||
"created": data.created,
|
||||
"workflow_id": data.workflow_id,
|
||||
"category": data.category,
|
||||
"tags": data.tags,
|
||||
})
|
||||
setValue(newvalue)
|
||||
setModalOpen(true)
|
||||
@@ -2377,7 +2528,7 @@ const CacheView = memo((props) => {
|
||||
height: 35,
|
||||
textTransform: 'none',
|
||||
|
||||
border: isAutomating ? `1px solid ${theme.palette.primary.main}` : null,
|
||||
border: isAutomating ? `1px solid ${green}` : null,
|
||||
}}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
@@ -2391,8 +2542,8 @@ const CacheView = memo((props) => {
|
||||
setShowAutomationMenu(true)
|
||||
}}
|
||||
>
|
||||
{isAutomating ? <RocketLaunchIcon style={{color: theme.palette.primary.main, marginRight: 10 }}/> : <RocketIcon style={{marginRight: 10, color: theme.palette.secondary.main, }} />}
|
||||
Automate (beta)
|
||||
{isAutomating ? <RocketLaunchIcon style={{color: green, marginRight: 10 }}/> : <RocketIcon style={{marginRight: 10, color: theme.palette.secondary.main, }} />}
|
||||
Automate
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
@@ -689,7 +689,6 @@ const ChatBot = (props) => {
|
||||
margin: "auto",
|
||||
maxWidth: "100%",
|
||||
minWidth: "100%",
|
||||
overflow: "hidden",
|
||||
fontSize: isMobile ? "1.3rem" : "1.0rem",
|
||||
}
|
||||
|
||||
|
||||
@@ -223,14 +223,14 @@ const DiscordChat = props => {
|
||||
</Button>
|
||||
<Typography style={{color: "white"}} variant="body2">{formMessage}</Typography>
|
||||
</div>
|
||||
<span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
{/* <span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
</span> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -349,14 +349,14 @@ const DocsGrid = props => {
|
||||
: null
|
||||
}
|
||||
|
||||
<span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
{/* <span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
</span> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -216,8 +216,8 @@ const EditWorkflow = (props) => {
|
||||
setNewWorkflowTags(responseJson.tags)
|
||||
}
|
||||
|
||||
if (selectedUsecases === []) {
|
||||
selectedUsecases = responseJson.usecase_ids
|
||||
if (selectedUsecases.length === 0) {
|
||||
setSelectedUsecases(responseJson.usecase_ids)
|
||||
}
|
||||
|
||||
innerWorkflow.id = responseJson.id
|
||||
@@ -979,7 +979,6 @@ const EditWorkflow = (props) => {
|
||||
color="primary"
|
||||
defaultValue={innerWorkflow.description}
|
||||
placeholder="Description"
|
||||
multiline
|
||||
label="Description"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
|
||||
@@ -432,7 +432,7 @@ const EnvironmentTab = memo((props) => {
|
||||
-e SHUFFLE_SWARM_CONFIG=run \\
|
||||
-e BASE_URL="${newUrl}" \\${addProxy ? `
|
||||
-e HTTPS_PROXY=IP:PORT \\` : ""}${skipPipeline ? `
|
||||
-e SHUFFLE_PIPELINE_URL=http://tenzir-node:5160 \ \n -e SHUFFLE_PIPELINE_STANDALONE=true \\` : ""}${!showDetection ? `
|
||||
-e SHUFFLE_PIPELINE_URL=http://tenzir-node:5160 \\\n -e SHUFFLE_PIPELINE_STANDALONE=true \\` : ""}${!showDetection ? `
|
||||
-v /tmp:/tmp \\` : ""}
|
||||
ghcr.io/shuffle/shuffle-orborus:latest
|
||||
`)
|
||||
@@ -1225,7 +1225,9 @@ const EnvironmentTab = memo((props) => {
|
||||
:
|
||||
(environment.running_ip === undefined ||
|
||||
environment.running_ip === null ||
|
||||
environment.running_ip.length === 0)
|
||||
environment.running_ip.length === 0) ||
|
||||
environment?.checkin === 0 ||
|
||||
Date.now() / 1000 - environment?.checkin > 180
|
||||
?
|
||||
<Chip
|
||||
key={index}
|
||||
|
||||
@@ -110,7 +110,11 @@ const [filesLoaded, setFilesLoaded] = useState(false);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={params.row.filename}
|
||||
title={<Typography variant="b1">
|
||||
{params?.row?.filename}<br/>
|
||||
{params?.row?.tags}
|
||||
</Typography>
|
||||
}
|
||||
placement="left"
|
||||
arrow
|
||||
>
|
||||
@@ -1580,8 +1584,8 @@ const [filesLoaded, setFilesLoaded] = useState(false);
|
||||
columns={columns}
|
||||
checkboxSelection
|
||||
disableRowSelectionOnClick
|
||||
selectionModel={selectedRows}
|
||||
onSelectionModelChange={(newSelection) => {
|
||||
rowSelectionModel={selectedRows}
|
||||
onRowSelectionModelChange={(newSelection) => {
|
||||
setSelectedRows(newSelection);
|
||||
}}
|
||||
sx={{
|
||||
|
||||
@@ -3,7 +3,6 @@ import React, { useState, useEffect } from "react";
|
||||
import { toast } from "react-toastify"
|
||||
import theme from '../theme.jsx';
|
||||
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
|
||||
@@ -24,8 +24,9 @@ const HealthPage = (props) => {
|
||||
const [liveExecutionsRange, setLiveExecutionsRange] = useState('1h'); // Default to 1h
|
||||
const [isHealthLoading, setIsHealthLoading] = useState(false); // Loading state for HealthBarChart
|
||||
const [isLiveExecutionsLoading, setIsLiveExecutionsLoading] = useState(false); // Loading state for LiveExecutionsChart
|
||||
const [isFixingOpensearchPrefix, setIsFixingOpensearchPrefix] = useState(false);
|
||||
|
||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
|
||||
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (import.meta.env.VITE_IS_SSR === "true");
|
||||
|
||||
const fetchHealthStats = useCallback(async () => {
|
||||
setIsHealthLoading(true); // Start loading for HealthBarChart
|
||||
@@ -364,19 +365,63 @@ const HealthPage = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleFixOpensearchPrefix = async () => {
|
||||
if (isFixingOpensearchPrefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsFixingOpensearchPrefix(true);
|
||||
try {
|
||||
const response = await fetch(`${globalUrl}/api/v1/health/opensearch-prefix`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (!response.ok || !data.success) {
|
||||
const reason = data && data.reason ? data.reason : "Failed to fix opensearch prefix";
|
||||
throw new Error(reason);
|
||||
}
|
||||
|
||||
const reindexed = data.reindexed ? data.reindexed.length : 0;
|
||||
const aliasUpdates = data.alias_updates ? data.alias_updates.length : 0;
|
||||
const deleted = data.deleted_indices ? data.deleted_indices.length : 0;
|
||||
toast.success(`Fixed opensearch prefix (reindexed ${reindexed}, aliases ${aliasUpdates}, deleted ${deleted})`);
|
||||
} catch (error) {
|
||||
console.error("Error fixing opensearch prefix:", error);
|
||||
toast.error(error.message || "Failed to fix opensearch prefix");
|
||||
} finally {
|
||||
setIsFixingOpensearchPrefix(false);
|
||||
}
|
||||
};
|
||||
|
||||
const healthBarData = updateChartData()
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ paddingTop: 30, width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
|
||||
{/* Health Bar Chart Section */}
|
||||
<ButtonGroup style={{ display: 'flex', margin: "auto", marginBottom: 10, width: 300, borderRadius: 30, background: "#000000" }}>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '24hr' ? '2px solid #FF8444' : 'none', background: "#000000", color: selectedRange === '24hr' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('24hr')} disabled={isHealthLoading}>24h</Button>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '7day' ? '2px solid #FF8444' : 'none', background: "#000000", color: selectedRange === '7day' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('7day')} disabled={isHealthLoading}>7d</Button>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '30d' ? '2px solid #FF8444' : 'none', background: "#000000", color: selectedRange === '30d' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('30d')} disabled={isHealthLoading}>30d</Button>
|
||||
<Button disabled variant="contained" style={{ flex: 1, borderBottom: selectedRange === '90d' ? '2px solid #FF8444' : 'none', background: "#000000", color: false === false ? "grey" : selectedRange === '90d' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('90d')} disabled={isHealthLoading}>90d</Button>
|
||||
<Button disabled variant="contained" style={{ flex: 1, borderBottom: selectedRange === '180d' ? '2px solid #FF8444' : 'none', background: "#000000", color: false === false ? "grey" : selectedRange === '180d' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('180d')} disabled={isHealthLoading}>180d</Button>
|
||||
</ButtonGroup>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, margin: "auto", marginBottom: 10 }}>
|
||||
<ButtonGroup style={{ display: 'flex', width: 300, borderRadius: 30, background: "#000000" }}>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '24hr' ? '2px solid #FF8444' : 'none', background: "#000000", color: selectedRange === '24hr' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('24hr')} disabled={isHealthLoading}>24h</Button>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '7day' ? '2px solid #FF8444' : 'none', background: "#000000", color: selectedRange === '7day' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('7day')} disabled={isHealthLoading}>7d</Button>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '30d' ? '2px solid #FF8444' : 'none', background: "#000000", color: selectedRange === '30d' ? '#FF8444' : '#cfd8dc', textTransform: 'none' }} onClick={() => filterDataByRange('30d')} disabled={isHealthLoading}>30d</Button>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '90d' ? '2px solid #FF8444' : 'none', background: "#000000", color: "grey", textTransform: 'none' }} onClick={() => filterDataByRange('90d')} disabled>90d</Button>
|
||||
<Button variant="contained" style={{ flex: 1, borderBottom: selectedRange === '180d' ? '2px solid #FF8444' : 'none', background: "#000000", color: "grey", textTransform: 'none' }} onClick={() => filterDataByRange('180d')} disabled>180d</Button>
|
||||
</ButtonGroup>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ background: '#1a1a1a', color: '#FF8444', textTransform: 'none' }}
|
||||
onClick={handleFixOpensearchPrefix}
|
||||
disabled={isFixingOpensearchPrefix}
|
||||
>
|
||||
{isFixingOpensearchPrefix ? 'Fixing Opensearch Prefix...' : 'Fix Opensearch Prefix'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Loading Bar for HealthBarChart */}
|
||||
{isHealthLoading && (
|
||||
|
||||
@@ -2,14 +2,13 @@ import React, { useEffect, useRef, useState, useContext, useCallback, useMemo, m
|
||||
import {
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
GridView as GridViewIcon,
|
||||
ShieldOutlined as ShieldOutlinedIcon,
|
||||
Add as AddIcon,
|
||||
BorderColor,
|
||||
Close as CloseIcon,
|
||||
ConstructionOutlined as ConstructionOutlinedIcon,
|
||||
Toc as TocIcon,
|
||||
Settings as SettingsIcon
|
||||
Settings as SettingsIcon,
|
||||
Business as BusinessIcon,
|
||||
HelpOutline as HelpOutlineIcon,
|
||||
MeetingRoom as MeetingRoomIcon,
|
||||
Search as SearchIcon,
|
||||
Add as AddIcon
|
||||
} from "@mui/icons-material";
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
@@ -44,24 +43,18 @@ import { useNavigate } from "react-router";
|
||||
import { getTheme } from "../theme.jsx";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
Business as BusinessIcon,
|
||||
Notifications as NotificationsIcon,
|
||||
HelpOutline as HelpOutlineIcon,
|
||||
MeetingRoom as MeetingRoomIcon,
|
||||
Lightbulb as LightbulbIcon,
|
||||
Search as SearchIcon
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import { toast } from "react-toastify";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
import Licensed from "./Licensed.jsx";
|
||||
|
||||
const ShuffleLogo = "/images/Shuffle_logo.png";
|
||||
const detectionIcon = "/icons/detection.svg";
|
||||
const documentationIcon = "/icons/documentation.svg";
|
||||
const ExpandMoreAndLessIcon = "/icons/expandMoreIcon.svg";
|
||||
|
||||
const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
|
||||
const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, SHUFFLE_VERSION }) => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, updateOrg, setUpdateOrg, setSearchBarModalOpen, searchBarModalOpen, logoutUrl, themeMode, handleThemeChange, isDocSearchModalOpen, setIsDocSearchModalOpen, supportEmail} = useContext(Context);
|
||||
@@ -690,7 +683,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
|
||||
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
|
||||
Version: <a href="https://github.com/Shuffle/Shuffle/releases" style={{ color: theme.palette.text.primary, textDecoration: "underline" }} target="_blank" rel="noreferrer">
|
||||
2.1.2
|
||||
{SHUFFLE_VERSION}
|
||||
</a>
|
||||
</Typography>
|
||||
</Menu>
|
||||
@@ -1930,72 +1923,48 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: 10,
|
||||
marginLeft: 5,
|
||||
padding: "0 8px",
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
{userdata?.licensed !== true && !userdata?.org_status?.includes("integration_partner") && expandLeftNav && !isProdStatusOn &&
|
||||
<div style={{display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
style={{marginBottom: 15, borderWidth: 2, }}
|
||||
onClick={() => {
|
||||
navigate("/admin?admin_tab=billingstats&ref=left_sidebar_upgrade")
|
||||
if (isCloud) {
|
||||
window.open("https://shuffler.io/contact?category=book_a_demo&ref=cloud", "_blank")
|
||||
} else {
|
||||
window.open("https://shuffler.io/contact?category=book_a_demo&ref=onprem", "_blank")
|
||||
}
|
||||
}}
|
||||
>
|
||||
Book a Demo
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
{!isCloud && activeOrgData?.old_org? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 20,
|
||||
padding: "4px 10px",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
borderRadius: 20,
|
||||
marginBottom: "14px",
|
||||
background: isProdStatusOn
|
||||
? "rgba(43, 192, 126, 0.1)"
|
||||
: "rgba(255, 82, 82, 0.1)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/admin?admin_tab=billingstats")
|
||||
}}
|
||||
>
|
||||
<span
|
||||
<>
|
||||
{userdata?.licensed !== true && !userdata?.org_status?.includes("integration_partner") && !isProdStatusOn && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
fullWidth
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
marginLeft: 10,
|
||||
background: isProdStatusOn ? "#2BC07E" : "#FD4C62",
|
||||
borderRadius: 999,
|
||||
display: expandLeftNav ? "inline" : "none",
|
||||
marginBottom: 30,
|
||||
marginRight: 8,
|
||||
height: 40,
|
||||
borderWidth: 1.5,
|
||||
borderRadius: 10,
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
textTransform: 'none',
|
||||
width: "95%" ,
|
||||
display: expandLeftNav ? 'flex' : 'none',
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
style={{
|
||||
fontFamily: "12px",
|
||||
opacity: 0.9,
|
||||
color: isProdStatusOn ? "#2BC07E" : "#FD4C62",
|
||||
}}
|
||||
>
|
||||
{expandLeftNav ? isProdStatusOn ? "Production" : "NOT production" : isProdStatusOn ? "ON" : "OFF"}
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
onClick={() => {
|
||||
if (isCloud) {
|
||||
window.open("https://shuffler.io/contact?category=book_a_demo&ref=cloud", "_blank")
|
||||
} else {
|
||||
window.open("https://shuffler.io/contact?category=book_a_demo&ref=onprem", "_blank")
|
||||
}
|
||||
}}
|
||||
>
|
||||
Book a Demo
|
||||
</Button>
|
||||
)}
|
||||
{!isCloud && (activeOrgData?.old_org || isProdStatusOn) && (
|
||||
|
||||
<Licensed
|
||||
expanded={expandLeftNav}
|
||||
licensed={isProdStatusOn}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
<Box ref={autocompleteRef}>
|
||||
<Autocomplete
|
||||
@@ -2321,4 +2290,4 @@ const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, s
|
||||
</Dialog>
|
||||
)
|
||||
)
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,357 @@
|
||||
|
||||
|
||||
import {
|
||||
CheckCircle,
|
||||
Warning as AlertCircle,
|
||||
Shield as ShieldIcon,
|
||||
Warning as WarningIcon,
|
||||
ChevronRight as ChevronRightIcon
|
||||
} from '@mui/icons-material';
|
||||
import { useContext, useState } from "react";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const Licensed = ({licensed, expanded, showBadge}) => {
|
||||
|
||||
const { themeMode } = useContext(Context);
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const theme = {
|
||||
light: {
|
||||
cardBg: '#ffffff',
|
||||
cardBgHover: '#f5f7fa',
|
||||
border: '#e1e4e8',
|
||||
textPrimary: '#1f2937',
|
||||
textSecondary: '#6b7280',
|
||||
textMuted: '#9ca3af',
|
||||
success: '#10b981',
|
||||
successBg: licensed ? 'rgba(5, 150, 105, 0.08)' : '#ddf4ff',
|
||||
successBorder: 'rgba(5, 150, 105, 0.2)',
|
||||
warning: '#d97706',
|
||||
warningBg: licensed ? '#fef3c7' : 'rgba(245, 158, 11, 0.08)',
|
||||
warningBorder: 'rgba(245, 158, 11, 0.2)',
|
||||
shadow: 'rgba(0, 0, 0, 0.05)',
|
||||
},
|
||||
dark: {
|
||||
cardBg: '#1a1a1a',
|
||||
cardBgHover: '#242424',
|
||||
border: '#30363d',
|
||||
textPrimary: '#e6edf3',
|
||||
textSecondary: '#8b949e',
|
||||
textMuted: '#6e7681',
|
||||
success: '#10b981',
|
||||
successBg: 'rgba(16, 185, 129, 0.1)',
|
||||
successBorder: 'rgba(16, 185, 129, 0.3)',
|
||||
warning: '#f59e0b',
|
||||
warningBg: 'rgba(245, 158, 11, 0.1)',
|
||||
warningBorder: 'rgba(245, 158, 11, 0.3)',
|
||||
shadow: 'rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
};
|
||||
|
||||
const colors = themeMode === "dark" ? theme.dark : theme.light;
|
||||
|
||||
const statusConfig = licensed
|
||||
? {
|
||||
icon: showBadge ? ShieldIcon : CheckCircle,
|
||||
text: showBadge ? 'Licensed' : 'Enterprise Licensed',
|
||||
shortText: 'Licensed',
|
||||
subtitle: 'Full access',
|
||||
color: colors.success,
|
||||
bgColor: colors.successBg,
|
||||
borderColor: colors.successBorder,
|
||||
description: 'All features unlocked',
|
||||
}
|
||||
: {
|
||||
icon: showBadge ? WarningIcon : AlertCircle,
|
||||
text: showBadge ? 'Unlicensed' : 'Activate License',
|
||||
shortText: 'Inactive',
|
||||
subtitle: 'Limited access',
|
||||
color: colors.warning,
|
||||
bgColor: colors.warningBg,
|
||||
borderColor: colors.warningBorder,
|
||||
description: 'Limited access',
|
||||
};
|
||||
|
||||
const Icon = statusConfig.icon;
|
||||
|
||||
// Display badge when showing license status only
|
||||
// Display full component for CTA to license activation
|
||||
if (showBadge) {
|
||||
return (
|
||||
<div
|
||||
onClick={()=> {navigate('/admin?admin_tab=billingstats');}}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
padding: '8px 14px',
|
||||
borderRadius: 12,
|
||||
cursor: 'pointer',
|
||||
backgroundColor: statusConfig.bgColor,
|
||||
border: `1.5px solid ${statusConfig.borderColor}`,
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
transform: isHovered ? 'translateY(-2px)' : 'translateY(0)',
|
||||
boxShadow: isHovered
|
||||
? `0 4px 12px ${colors.shadow}, 0 0 0 3px ${statusConfig.bgColor}`
|
||||
: `0 2px 4px ${colors.shadow}`,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* Shine effect on hover */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: `linear-gradient(90deg, transparent, rgba(255,255,255,${themeMode === "dark" ? '0.1' : '0.2'}), transparent)`,
|
||||
transition: 'left 0.5s',
|
||||
left: isHovered ? '100%' : '-100%',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Status indicator with pulse */}
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: statusConfig.color,
|
||||
boxShadow: `0 0 8px ${statusConfig.color}`,
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
}}
|
||||
/>
|
||||
{/* Pulse ring */}
|
||||
<span
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: statusConfig.color,
|
||||
opacity: 0.75,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
style={{
|
||||
color: statusConfig.color,
|
||||
flexShrink: 0,
|
||||
fontSize: 16,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 13,
|
||||
fontWeight: 700,
|
||||
color: statusConfig.color,
|
||||
lineHeight: 1.2,
|
||||
letterSpacing: '0.2px',
|
||||
}}
|
||||
>
|
||||
{statusConfig.text}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 10,
|
||||
fontWeight: 600,
|
||||
color: statusConfig.color,
|
||||
opacity: 0.7,
|
||||
lineHeight: 1,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.5px',
|
||||
}}
|
||||
>
|
||||
{statusConfig.subtitle}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ChevronRightIcon
|
||||
style={{
|
||||
color: statusConfig.color,
|
||||
opacity: 0.6,
|
||||
flexShrink: 0,
|
||||
fontSize: 14,
|
||||
transform: isHovered ? 'translateX(2px)' : 'translateX(0)',
|
||||
transition: 'all 0.2s',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={()=> {navigate('/admin?admin_tab=billingstats');}}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: expanded ? 12 : 8,
|
||||
padding: expanded ? '10px 14px' : '10px',
|
||||
marginBottom: 20,
|
||||
marginRight: 12,
|
||||
borderRadius: 10,
|
||||
cursor: 'pointer',
|
||||
backgroundColor: isHovered ? colors.cardBgHover : colors.cardBg,
|
||||
border: `1.5px solid ${licensed ? statusConfig.color : colors.border}`,
|
||||
boxShadow: `0 1px 3px ${colors.shadow}`,
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
transform: isHovered ? 'translateY(-1px)' : 'translateY(0)',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: `linear-gradient(135deg, ${statusConfig.bgColor} 0%, transparent 100%)`,
|
||||
opacity: 0.5,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
position: 'relative',
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
{expanded && (
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
style={{
|
||||
color: statusConfig.color,
|
||||
flexShrink: 0,
|
||||
fontSize: 18,
|
||||
}}
|
||||
/>
|
||||
{!licensed && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: '50%',
|
||||
border: `2px solid ${statusConfig.color}`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Text content */}
|
||||
{expanded && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
minWidth: 0,
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
color: colors.textPrimary,
|
||||
lineHeight: 1.2,
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{statusConfig.text}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 11,
|
||||
fontWeight: 500,
|
||||
color: colors.textSecondary,
|
||||
lineHeight: 1.2,
|
||||
}}
|
||||
>
|
||||
{statusConfig.description}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!expanded && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: 11,
|
||||
fontWeight: 700,
|
||||
color: statusConfig.color,
|
||||
letterSpacing: '0.5px',
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
{licensed ? 'ON' : 'OFF'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Arrow indicator (only when expanded) */}
|
||||
{expanded && (
|
||||
<ChevronRightIcon
|
||||
style={{
|
||||
color: colors.textMuted,
|
||||
flexShrink: 0,
|
||||
fontSize: 16,
|
||||
opacity: isHovered ? 1 : 0.5,
|
||||
transform: isHovered ? 'translateX(2px)' : 'translateX(0)',
|
||||
transition: 'all 0.2s',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Licensed;
|
||||
@@ -748,7 +748,7 @@ const Navbar = (props) => {
|
||||
// isMobile,
|
||||
serverside,
|
||||
billingInfo,
|
||||
|
||||
SHUFFLE_VERSION,
|
||||
notifications,
|
||||
} = props;
|
||||
|
||||
@@ -2624,7 +2624,7 @@ const Navbar = (props) => {
|
||||
margin: 0
|
||||
}}
|
||||
>
|
||||
Version 2.0.2
|
||||
Version {SHUFFLE_VERSION}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Menu>
|
||||
|
||||
@@ -1450,7 +1450,6 @@ const Header = (props) => {
|
||||
color="transparent"
|
||||
elevation={0}
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
boxShadow: "none",
|
||||
minHeight: 68,
|
||||
maxHeight: 68,
|
||||
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Icon as IconButton,
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Save as SaveIcon,
|
||||
|
||||
@@ -67,7 +67,6 @@ import {
|
||||
Cached as CachedIcon,
|
||||
DirectionsRun as DirectionsRunIcon,
|
||||
Add as AddIcon,
|
||||
Polymer as PolymerIcon,
|
||||
FormatListNumbered as FormatListNumberedIcon,
|
||||
Create as CreateIcon,
|
||||
PlayArrow as PlayArrowIcon,
|
||||
|
||||
@@ -25,7 +25,7 @@ const Priority = (props) => {
|
||||
const { globalUrl, clickedFromOrgTab,userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props;
|
||||
const { themeMode, supportEmail } = useContext(Context);
|
||||
const theme = getTheme(themeMode);
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
let navigate = useNavigate();
|
||||
|
||||
if (window.location.pathname === "/workflows") {
|
||||
@@ -120,7 +120,7 @@ const Priority = (props) => {
|
||||
const srcSize = realignedSrc ? 35 : 30
|
||||
const dstSize = realignedDst ? 35 : 30
|
||||
return (
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : clickedFromOrgTab ?null:"1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, marginTop: 10, marginBottom: 10, padding: clickedFromOrgTab ? 24:15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: clickedFromOrgTab ? theme.palette.backgroundColor : theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : clickedFromOrgTab ?null:"1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, marginTop: 10, marginBottom: 10, padding: clickedFromOrgTab ? 24:15, minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: clickedFromOrgTab ? theme.palette.backgroundColor : theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{flex: 2, overflow: "hidden",}}>
|
||||
<span style={{display: "flex", }}>
|
||||
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, color: theme.palette.text.primary}}/> : null}
|
||||
|
||||
@@ -168,11 +168,13 @@ const RunDetectionTest = (props) => {
|
||||
return foundCorrect
|
||||
}
|
||||
|
||||
console.log("DETECTION CHECK: ", haveDetectionPipelines().length, ticketWebhook, detectionWorkflowId, detectionTestRunning)
|
||||
|
||||
return (
|
||||
<div style={{display: "flex", }}>
|
||||
<ButtonGroup style={{minWidth: 150, maxWidth: 225,}}>
|
||||
<Tooltip title={
|
||||
`Run a detection test with Sigma rules on your Tenzir Orborus instance. Requires: TCPC Syslog- & Sigma pipeline\n\nEnvironments: ${haveDetectionPipelines().join(", ")}`
|
||||
`Run a detection test with Sigma rules on your Tenzir Orborus instance. Requires: TCP Syslog- & Sigma pipeline\n\nEnvironments: ${haveDetectionPipelines().join(", ")}`
|
||||
} style={{}} aria-label={"Run detection test"}>
|
||||
<div>
|
||||
<Button
|
||||
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
Send as SendIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import { DataGrid, GridValueGetterParams } from '@mui/x-data-grid'
|
||||
import { DataGrid } from '@mui/x-data-grid'
|
||||
import {
|
||||
Search as SearchIcon,
|
||||
} from "@mui/icons-material";
|
||||
@@ -78,7 +78,7 @@ const RuntimeDebugger = (props) => {
|
||||
const [ignoreOrg, setIgnoreOrg] = useState(false)
|
||||
const [searchLoading, setSearchLoading] = useState(false)
|
||||
const [rowCursor, setCursor] = useState("")
|
||||
const [rowsPerPage, setRowsPerPage] = useState(10)
|
||||
const [rowsPerPage, setRowsPerPage] = useState(20)
|
||||
const [maxExecutionCount, setMaxExecutionCount] = useState(50)
|
||||
const [resultRows, setResultRows] = useState([])
|
||||
const [selectedWorkflowExecutions, setSelectedWorkflowExecutions] = useState([])
|
||||
@@ -219,6 +219,10 @@ const RuntimeDebugger = (props) => {
|
||||
if (data.runs[key].completed_at === 0 || data.runs[key].completed_at === null) {
|
||||
data.runs[key].endTimestamp = ""
|
||||
}
|
||||
|
||||
if (data.runs[key]?.workflow_id === data.runs[key]?.execution_id && data.runs[key].type === "AGENT") {
|
||||
data.runs[key].workflow.name = "Agent Execution"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -359,6 +363,8 @@ const RuntimeDebugger = (props) => {
|
||||
} else if (source === "single_action" || source == "single_api" || source === "direct_api") {
|
||||
foundSource = <SendIcon color="secondary" style={{height: imageSize-5, }} />
|
||||
source = "Single API call"
|
||||
} else if (params?.row?.type === "AGENT") {
|
||||
foundSource = <img src={theme.palette.singulBlackWhite} alt="agent" style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
|
||||
} else {
|
||||
source = "manual"
|
||||
}
|
||||
@@ -626,19 +632,28 @@ const RuntimeDebugger = (props) => {
|
||||
return (
|
||||
<div style={{display: "flex", }}>
|
||||
<Tooltip arrow placement="left" title={
|
||||
<Typography variant="body2" style={{whiteSpace: "pre-line", padding: 10, }}>
|
||||
Workflow result: {errorReason}<br/><br/>
|
||||
{params.row.result !== null && params.row.result !== undefined && params.row.result !== "" ?
|
||||
params.row.result
|
||||
:
|
||||
null
|
||||
}
|
||||
</Typography>
|
||||
params.row.type === "AGENT" ?
|
||||
"See agent result"
|
||||
:
|
||||
<Typography variant="body2" style={{whiteSpace: "pre-line", padding: 10, }}>
|
||||
Workflow result: {errorReason}<br/><br/>
|
||||
{params.row.result !== null && params.row.result !== undefined && params.row.result !== "" ?
|
||||
params.row.result
|
||||
:
|
||||
null
|
||||
}
|
||||
</Typography>
|
||||
}>
|
||||
<span style={{backgroundColor: !hasError ? "inherit" : "rgba(244,0,0,0.45)", display: "flex", }}>
|
||||
<Link href={`/workflows/${params.row.workflow.id}?execution_id=${params.row.id}`} target="_blank" rel="noopener noreferrer">
|
||||
<OpenInNewIcon fontSize="small" style={{marginTop: 7, }} />
|
||||
</Link>
|
||||
{params.row.type === "AGENT" ?
|
||||
<Link href={`/agents?execution_id=${params.row.execution_id}&authorization=${params.row.authorization}`} target="_blank" rel="noopener noreferrer">
|
||||
<OpenInNewIcon fontSize="small" style={{marginTop: 7, }} />
|
||||
</Link>
|
||||
:
|
||||
<Link href={`/workflows/${params.row.workflow.id}?execution_id=${params.row.id}`} target="_blank" rel="noopener noreferrer">
|
||||
<OpenInNewIcon fontSize="small" style={{marginTop: 7, }} />
|
||||
</Link>
|
||||
}
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip arrow title={`Force continue workflow. Only workflows for workflows in EXECUTING state. This is NOT a rerun, but way for Shuffle to figure out the next steps automatically. If the execution doesn't finish even after trying this, please contact ${supportEmail}`}>
|
||||
@@ -1069,7 +1084,10 @@ const RuntimeDebugger = (props) => {
|
||||
).replaceAll("_", " ");
|
||||
return newname;
|
||||
}}
|
||||
options={workflows}
|
||||
options={[{
|
||||
"name": "Agent Runs",
|
||||
"id": "AGENT",
|
||||
}].concat(workflows)}
|
||||
fullWidth
|
||||
style={{
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
|
||||
@@ -73,15 +73,18 @@ const SchedulesTab = memo((props) => {
|
||||
setWorkflows(responseJson || []);
|
||||
|
||||
for (var i = 0; i < responseJson?.length; i++) {
|
||||
if (responseJson[i].background_processing === true && responseJson[i].name.toLowerCase().includes("ingest tickets") && responseJson[i].triggers !== undefined) {
|
||||
if (responseJson[i].background_processing === true && (responseJson[i].name.toLowerCase().includes("ticket") || responseJson[i].name.toLowerCase().includes("ingest")) && responseJson[i].triggers !== undefined) {
|
||||
|
||||
for (var triggerkey in responseJson[i].triggers) {
|
||||
if (responseJson[i].triggers[triggerkey].trigger_type === "WEBHOOK") {
|
||||
setDetectionWorkflowId(responseJson[i].id)
|
||||
setTicketWebhook(`${globalUrl}/api/v1/hooks/webhook_${responseJson[i].triggers[triggerkey].id}`)
|
||||
setNewPipelineValue(`export live=true | sigma /tmp/sigma_rules | to ${globalUrl}/api/v1/hooks/webhook_${responseJson[i].triggers[triggerkey].id}`)
|
||||
break;
|
||||
//console.log("TRIGGER: ", responseJson[i].triggers[triggerkey].trigger_type)
|
||||
if (responseJson[i].triggers[triggerkey].trigger_type !== "WEBHOOK") {
|
||||
continue
|
||||
}
|
||||
|
||||
setDetectionWorkflowId(responseJson[i].id)
|
||||
setTicketWebhook(`${globalUrl}/api/v1/hooks/webhook_${responseJson[i].triggers[triggerkey].id}`)
|
||||
setNewPipelineValue(`export live=true | sigma /tmp/sigma_rules | to ${globalUrl}/api/v1/hooks/webhook_${responseJson[i].triggers[triggerkey].id}`)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect, useRef, useContext } from 'react';
|
||||
|
||||
import {getTheme} from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { useNavigate, Link, useParams, useLocation } from "react-router-dom";
|
||||
import { toast } from "react-toastify"
|
||||
|
||||
import {
|
||||
@@ -102,7 +102,8 @@ const SearchData = props => {
|
||||
// setModalOpen(false);
|
||||
const trimmedValue = inputValue.trim();
|
||||
if (trimmedValue !== '') {
|
||||
navigate(`/search?q=${trimmedValue}`, { state: trimmedValue, replace: true });
|
||||
// Navigate to the search page (keep modal open by passing state)
|
||||
navigate(`/search`, { state: trimmedValue, replace: true });
|
||||
setSearchBarModalOpen(false);
|
||||
}
|
||||
}
|
||||
@@ -916,7 +917,7 @@ const SearchData = props => {
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid style={{ textAlign: "end", width: "100%", textTransform: 'capitalize', }}>
|
||||
<Link to="/search" style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||
<Link to="/search" onClick={handleLinkClick} style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||
<Button style={{ textAlign: "center", textTransform: 'capitalize' }}>
|
||||
See More
|
||||
</Button>
|
||||
|
||||
@@ -29,7 +29,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b
|
||||
const AppGrid = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
|
||||
@@ -395,8 +395,9 @@ const WorkflowTemplatePopup = (props) => {
|
||||
}
|
||||
|
||||
console.log("SRCAPP: ", srcapp, "DSTAPP: ", dstapp)
|
||||
if (srcapp === undefined || srcapp === null) {
|
||||
srcapp = ""
|
||||
let effectiveSrcapp = srcapp
|
||||
if (effectiveSrcapp === undefined || effectiveSrcapp === null) {
|
||||
effectiveSrcapp = ""
|
||||
}
|
||||
|
||||
if ((srcapp !== undefined && srcapp !== null && srcapp.includes(":default")) || (dstapp !== undefined && dstapp !== null && dstapp.includes(":default"))) {
|
||||
|
||||
@@ -87,11 +87,12 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
selector: `node[type="ACTION"]`,
|
||||
css: {
|
||||
shape: "roundrectangle",
|
||||
"background-color": "#213243",
|
||||
"border-color": "#81c784",
|
||||
"background-color": theme.palette.cytoscapeBackgroundColor,
|
||||
"border-width": "2px",
|
||||
"border-color": "#02CB70",
|
||||
"background-width": "100%",
|
||||
"background-height": "100%",
|
||||
"border-radius": "5px",
|
||||
"corner-radius": 12,
|
||||
"z-index": 5001,
|
||||
},
|
||||
},
|
||||
@@ -170,7 +171,9 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
"background-width": "100%",
|
||||
"background-height": "100%",
|
||||
|
||||
"background-color": "data(iconBackground)",
|
||||
"background-color": function(element) {
|
||||
return element.data("iconBackground") || undefined;
|
||||
},
|
||||
"background-fill": "data(fillstyle)",
|
||||
"background-gradient-direction": "to-bottom-right",
|
||||
"background-gradient-stop-colors": "data(fillGradient)",
|
||||
@@ -187,7 +190,9 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
"font-size": "0px",
|
||||
"background-width": "75%",
|
||||
"background-height": "75%",
|
||||
"background-color": "data(iconBackground)",
|
||||
"background-color": function(element) {
|
||||
return element.data("iconBackground") || undefined;
|
||||
},
|
||||
"background-fill": "data(fillstyle)",
|
||||
"background-gradient-direction": "to-right",
|
||||
"background-gradient-stop-colors": "data(fillGradient)",
|
||||
@@ -214,6 +219,8 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
css: {
|
||||
"background-image": "data(large_image)",
|
||||
"text-halign": "right",
|
||||
"background-width": "85%",
|
||||
"background-height": "85%",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -269,7 +276,9 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
"font-size": "0px",
|
||||
border: "1px solid rgba(255,255,255,0.9)",
|
||||
"background-image": "data(icon)",
|
||||
"background-color": "data(iconBackground)",
|
||||
"background-color": function(element) {
|
||||
return element.data("iconBackground") || null;
|
||||
} || "null",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -329,43 +338,6 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
"background-height": "100%",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[app_id="shuffle_agent"]`,
|
||||
css: {
|
||||
"shape": function(element) {
|
||||
return "roundrectangle"
|
||||
},
|
||||
"height": "74px",
|
||||
"width": "222px",
|
||||
"background-image": "data(large_image)",
|
||||
"label": function(element) {
|
||||
var elementname = element.data("label")
|
||||
if (elementname === null || elementname === undefined) {
|
||||
return ""
|
||||
}
|
||||
|
||||
elementname = elementname.replaceAll("_", " ", -1)
|
||||
|
||||
if (elementname.length > 15) {
|
||||
elementname = elementname.substring(0, 15) + ".."
|
||||
}
|
||||
|
||||
return elementname
|
||||
},
|
||||
"background-width": "65px",
|
||||
"background-height": "65px",
|
||||
"background-position-x": "20px",
|
||||
"background-repeat": "no-repeat",
|
||||
"background-opacity": "0.5",
|
||||
|
||||
"font-size": "14px",
|
||||
"text-halign": "center",
|
||||
"text-valign": "center",
|
||||
"text-margin-x": "20px",
|
||||
"text-margin-y": "0px",
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node[!is_valid]",
|
||||
css: {
|
||||
@@ -384,9 +356,9 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
{
|
||||
selector: ".skipped-highlight",
|
||||
css: {
|
||||
"background-color": "grey",
|
||||
"background-color": "transparent",
|
||||
"border-color": "grey",
|
||||
"border-width": "8px",
|
||||
"border-width": "3px",
|
||||
"transition-property": "background-color",
|
||||
"transition-duration": "0.5s",
|
||||
},
|
||||
@@ -435,7 +407,7 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
selector: ".executing-highlight",
|
||||
css: {
|
||||
//"background-color": "#ffef47",
|
||||
"border-color": "#FECC00",
|
||||
"border-color": "#FFC633",
|
||||
"border-width": "8px",
|
||||
"transition-property": "border-width",
|
||||
"transition-duration": "0.25s",
|
||||
@@ -477,8 +449,8 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
selector: "edge.executing-highlight",
|
||||
css: {
|
||||
width: "5px",
|
||||
"target-arrow-color": "#FECC00",
|
||||
"line-color": "#FECC00",
|
||||
"target-arrow-color": "#FFC633",
|
||||
"line-color": "#FFC633",
|
||||
"transition-property": "line-color, width",
|
||||
"transition-duration": "0.25s",
|
||||
},
|
||||
@@ -535,6 +507,8 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
{
|
||||
selector: ".eh-preview, .eh-ghost-edge",
|
||||
style: {
|
||||
"curve-style": "unbundled-bezier",
|
||||
"control-point-step-size": 60,
|
||||
"background-color": "#337ab7",
|
||||
"line-color": "#337ab7",
|
||||
"target-arrow-color": "#337ab7",
|
||||
@@ -602,8 +576,26 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
{
|
||||
selector: "node:selected",
|
||||
css: {
|
||||
"border-color": "#f86a3e",
|
||||
"border-width": "7px",
|
||||
"border-color": "#FF8444",
|
||||
"border-width": "3px",
|
||||
"background-color": "transparent",
|
||||
},
|
||||
},
|
||||
{
|
||||
// This is to show the underlay orange effect on action on selection
|
||||
selector: 'node[type="ACTION"]:selected',
|
||||
css: {
|
||||
"underlay-color": "rgba(255, 132, 68, 0.15)",
|
||||
"underlay-padding": 10,
|
||||
"underlay-opacity": 0.3,
|
||||
"underlay-corner-radius": 16,
|
||||
},
|
||||
},
|
||||
{
|
||||
// For the startNode
|
||||
selector: 'node[?isStartNode][app_id != "shuffle_agent"]:selected',
|
||||
css: {
|
||||
"underlay-shape": "ellipse",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -656,6 +648,78 @@ export default function defaultCytoscapeStyle(theme, apps) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'node[flowOrientation = "horizontal"][?small_image], node[flowOrientation = "horizontal"][?large_image]',
|
||||
css: {
|
||||
"text-halign": "center",
|
||||
"text-valign": "bottom",
|
||||
"text-margin-x": "0px",
|
||||
"text-margin-y": "12px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'node[flowOrientation = "vertical"][?small_image], node[flowOrientation = "vertical"][?large_image]',
|
||||
css: {
|
||||
"text-halign": "right",
|
||||
"text-valign": "center",
|
||||
"text-margin-x": "15px",
|
||||
"text-margin-y": "0px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'node[?isStartNode], node[?isStartNode][flowOrientation = "vertical"]',
|
||||
css: {
|
||||
"text-halign": "right",
|
||||
"text-valign": "center",
|
||||
"text-margin-x": "15px",
|
||||
"text-margin-y": "0px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'node[?isStartNode][flowOrientation = "horizontal"]',
|
||||
css: {
|
||||
"text-halign": "center",
|
||||
"text-valign": "bottom",
|
||||
"text-margin-x": "0px",
|
||||
"text-margin-y": "12px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[app_id="shuffle_agent"]`,
|
||||
css: {
|
||||
"shape": function(element) {
|
||||
return "roundrectangle"
|
||||
},
|
||||
"height": "74px",
|
||||
"width": "222px",
|
||||
"background-image": "data(large_image)",
|
||||
"label": function(element) {
|
||||
var elementname = element.data("label")
|
||||
if (elementname === null || elementname === undefined) {
|
||||
return ""
|
||||
}
|
||||
|
||||
elementname = elementname.replaceAll("_", " ", -1)
|
||||
|
||||
if (elementname.length > 15) {
|
||||
elementname = elementname.substring(0, 15) + ".."
|
||||
}
|
||||
|
||||
return elementname
|
||||
},
|
||||
"background-width": "65px",
|
||||
"background-height": "65px",
|
||||
"background-position-x": "20px",
|
||||
"background-repeat": "no-repeat",
|
||||
"background-color": "#212121",
|
||||
"font-size": "14px",
|
||||
"text-halign": "center",
|
||||
"text-valign": "center",
|
||||
"text-margin-x": "20px",
|
||||
"text-margin-y": "0px",
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[name="switch"]`,
|
||||
css: {
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === "localhost" ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === "[::1]" ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
"This web app is being served cache-first by a service " +
|
||||
"worker. To learn more, visit https://goo.gl/SC7cgQ"
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then((registration) => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === "installed") {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log("New content is available; please refresh.");
|
||||
|
||||
// Execute callback
|
||||
if (config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log("Content is cached for offline use.");
|
||||
|
||||
// Execute callback
|
||||
if (config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error during service worker registration:", error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then((response) => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if (
|
||||
response.status === 404 ||
|
||||
response.headers.get("content-type").indexOf("javascript") === -1
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
"No internet connection found. App is running in offline mode."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -153,10 +153,20 @@ const theme = createTheme(adaptV4Theme({
|
||||
|
||||
export default theme;
|
||||
|
||||
export const getTheme = (themeMode, brandColor) =>
|
||||
createTheme({
|
||||
export const getTheme = (themeMode, brandColor) => {
|
||||
// Handle "system" mode by checking user's system preference
|
||||
let resolvedMode = themeMode;
|
||||
if (themeMode === "system" || !themeMode) {
|
||||
resolvedMode = window?.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
|
||||
}
|
||||
// Ensure mode is only "dark" or "light"
|
||||
if (resolvedMode !== "dark" && resolvedMode !== "light") {
|
||||
resolvedMode = "dark";
|
||||
}
|
||||
|
||||
return createTheme({
|
||||
palette: {
|
||||
mode: themeMode,
|
||||
mode: resolvedMode,
|
||||
main: brandColor || "#FF8544",
|
||||
primary: {
|
||||
main: brandColor || "#FF8544",
|
||||
@@ -167,32 +177,35 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
contrastText:"#000000",
|
||||
},
|
||||
text: {
|
||||
primary: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
|
||||
secondary: themeMode === "dark" ? "#9E9E9E" : "#616161",
|
||||
primary: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A",
|
||||
secondary: resolvedMode === "dark" ? "#9E9E9E" : "#616161",
|
||||
},
|
||||
type: themeMode,
|
||||
inputColor: themeMode === "dark" ? "rgba(39,41,45,1)" : "rgba(245, 245, 245, 1)",
|
||||
textColor: themeMode === "dark" ? "#F1F1F1" : "#1A1A1A",
|
||||
textPrimary: themeMode === "dark" ? "rgba(255, 255, 255, 0.8)" : "rgba(26, 26, 26, 0.8)",
|
||||
surfaceColor: themeMode === "dark" ? "#27292d" : "#EFEFEF",
|
||||
platformColor: themeMode === "dark" ? "#212121" : "#ffffff",
|
||||
backgroundColor: themeMode === "dark" ? "#1a1a1a" : "#f1f1f1",
|
||||
distributionColor: themeMode === "dark" ? "#40E0D0" : "#008080",
|
||||
cardBackgroundColor: themeMode === "dark" ? "#1e1e1e" : "#eaeaea",
|
||||
cardHoverColor: themeMode === "dark" ? "#323232" : "#F0F0F0",
|
||||
hoverColor: themeMode === "dark" ? "#323232" : "#D6D6D6",
|
||||
usecaseCardColor: themeMode === "dark" ? "#2f2f2f" : "rgba(245, 245, 245, 1)",
|
||||
usecaseCardHoverColor: themeMode === "dark" ? "#2F2F2F" : "rgba(245, 245, 245, 1)",
|
||||
usecaseDialogFieldColor: themeMode === "dark" ? "#2B2B2B" : "#F5F5F5",
|
||||
accentColor: themeMode === "dark" ? "#ff8544" : "#ff8544",
|
||||
green: themeMode === "dark" ? "#5cc879" : "#008000",
|
||||
defaultBorder: themeMode === "dark" ? '1px solid #494949' : '1px solid #CCCCCC',
|
||||
type: resolvedMode,
|
||||
inputColor: resolvedMode === "dark" ? "rgba(39,41,45,1)" : "rgba(245, 245, 245, 1)",
|
||||
textColor: resolvedMode === "dark" ? "#F1F1F1" : "#1A1A1A",
|
||||
textPrimary: resolvedMode === "dark" ? "rgba(255, 255, 255, 0.8)" : "rgba(26, 26, 26, 0.8)",
|
||||
surfaceColor: resolvedMode === "dark" ? "#27292d" : "#EFEFEF",
|
||||
platformColor: resolvedMode === "dark" ? "#212121" : "#ffffff",
|
||||
backgroundColor: resolvedMode === "dark" ? "#1a1a1a" : "#f1f1f1",
|
||||
cytoscapeBackgroundColor: resolvedMode === "dark" ? "#161616" : "#f5f5f5",
|
||||
distributionColor: resolvedMode === "dark" ? "#40E0D0" : "#008080",
|
||||
cardBackgroundColor: resolvedMode === "dark" ? "#1e1e1e" : "#eaeaea",
|
||||
cardHoverColor: resolvedMode === "dark" ? "#323232" : "#F0F0F0",
|
||||
hoverColor: resolvedMode === "dark" ? "#323232" : "#D6D6D6",
|
||||
usecaseCardColor: resolvedMode === "dark" ? "#2f2f2f" : "rgba(245, 245, 245, 1)",
|
||||
usecaseCardHoverColor: resolvedMode === "dark" ? "#2F2F2F" : "rgba(245, 245, 245, 1)",
|
||||
usecaseDialogFieldColor: resolvedMode === "dark" ? "#2B2B2B" : "#F5F5F5",
|
||||
accentColor: resolvedMode === "dark" ? "#ff8544" : "#ff8544",
|
||||
green: resolvedMode === "dark" ? "#5cc879" : "#008000",
|
||||
defaultBorder: resolvedMode === "dark" ? '1px solid #494949' : '1px solid #CCCCCC',
|
||||
linkColor: brandColor === "#ff8544" ? "#f86a3e" : brandColor,
|
||||
slateGrayColor: resolvedMode === "dark" ? "#494949" : "#CCCCCC",
|
||||
parsedAppPaperColor: resolvedMode === "dark" ? "#2f2f2f" : "#CCCCCC",
|
||||
|
||||
borderRadius: 10,
|
||||
loaderColor: themeMode === "dark" ? "#1a1a1a" : "#E0E0E0",
|
||||
loaderColor: resolvedMode === "dark" ? "#1a1a1a" : "#E0E0E0",
|
||||
jsonIconStyle: "round",
|
||||
jsonTheme: themeMode === "dark" ? "summerfruit" : {
|
||||
jsonTheme: resolvedMode === "dark" ? "summerfruit" : {
|
||||
base00: "#ffffff", // background
|
||||
base01: "#f0f0f0", // very light grey
|
||||
base02: "#f5f5f5", // light grey
|
||||
@@ -212,59 +225,66 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
},
|
||||
jsonCollapseStringsAfterLength: 100,
|
||||
drawer: {
|
||||
backgroundColor: themeMode === "dark" ? "#262626" : "#f9f9f9"
|
||||
backgroundColor: resolvedMode === "dark" ? "#262626" : "#f9f9f9"
|
||||
},
|
||||
actionSidebarField: {
|
||||
backgroundColor: resolvedMode === "dark" ? "#2F2F2F" : "#F1F1F1",
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
|
||||
borderRadius: 8,
|
||||
height: 40,
|
||||
border: "none",
|
||||
},
|
||||
reactJsonStyle: {
|
||||
padding: 5,
|
||||
width: "98%",
|
||||
borderRadius: 5,
|
||||
border: themeMode === "dark" ? "1px solid rgba(255,255,255,0.7)" : "1px solid rgba(0,0,0,0.3)",
|
||||
backgroundColor: themeMode === "dark"
|
||||
border: resolvedMode === "dark" ? "1px solid rgba(255,255,255,0.7)" : "1px solid rgba(0,0,0,0.3)",
|
||||
backgroundColor: resolvedMode === "dark"
|
||||
? "#1A1A1A"
|
||||
: "#f1f1f1",
|
||||
color: themeMode === "dark"
|
||||
color: resolvedMode === "dark"
|
||||
? "#F1F1F1"
|
||||
: "#1A1A1A",
|
||||
overflowX: "auto",
|
||||
},
|
||||
textFieldStyle: {
|
||||
backgroundColor: themeMode === "dark" ? "#212121" : "#FFFFFF",
|
||||
color: themeMode === "dark" ? "#ffffff" : "#000000",
|
||||
backgroundColor: resolvedMode === "dark" ? "#212121" : "#FFFFFF",
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
|
||||
borderRadius: "5px",
|
||||
height: 40,
|
||||
border: themeMode === "dark" ? "1px solid #4D4D4D" : "1px solid #E0E0E0",
|
||||
border: resolvedMode === "dark" ? "1px solid #4D4D4D" : "1px solid #E0E0E0",
|
||||
},
|
||||
DialogStyle: {
|
||||
backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff",
|
||||
backgroundColor: resolvedMode === "dark" ? "#212121" : "#ffffff",
|
||||
borderRadius: 2,
|
||||
boxShadow: themeMode === "dark" ? "0px 0px 10px 0px rgba(0,0,0,0.75)" : "0px 0px 10px 0px rgba(0,0,0,0.2)",
|
||||
border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
|
||||
boxShadow: resolvedMode === "dark" ? "0px 0px 10px 0px rgba(0,0,0,0.75)" : "0px 0px 10px 0px rgba(0,0,0,0.2)",
|
||||
border: resolvedMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
|
||||
},
|
||||
innerTextfieldStyle: {
|
||||
height: 40,
|
||||
fontSize: 16,
|
||||
backgroundColor: themeMode === "dark" ? "#212121" : "#f5f5f5",
|
||||
backgroundColor: resolvedMode === "dark" ? "#212121" : "#f5f5f5",
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff",
|
||||
color: themeMode === "dark" ? "#ffffff" : "#000000",
|
||||
border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
|
||||
backgroundColor: resolvedMode === "dark" ? "#212121" : "#ffffff",
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
|
||||
border: resolvedMode === "dark" ? "1px solid #494949" : "1px solid #cccccc",
|
||||
},
|
||||
chipStyle: {
|
||||
backgroundColor: themeMode === "dark" ? "#333333" : "#F5F5F5",
|
||||
borderColor: themeMode === "dark" ? "#444444" : "#E0E0E0",
|
||||
color: themeMode === "dark" ? "#FFFFFF" : "#333333",
|
||||
backgroundColor: resolvedMode === "dark" ? "#333333" : "#F5F5F5",
|
||||
borderColor: resolvedMode === "dark" ? "#444444" : "#E0E0E0",
|
||||
color: resolvedMode === "dark" ? "#FFFFFF" : "#333333",
|
||||
},
|
||||
defaultImage: "/images/no_image.png",
|
||||
singulOrange: "/images/singul_orange.png",
|
||||
singulGreen: "/images/singul_green.png",
|
||||
singulBlackWhite: "/images/singul_black_white.png",
|
||||
scrollbarColor: themeMode === "dark" ? "#494949 #2f2f2f": "#c1c1c1 #f1f1f1",
|
||||
scrollbarColorTransparent: themeMode === "dark" ? '#494949 transparent': "#c1c1c1 transparent",
|
||||
singulBlackWhite: "/icons/workflow-page/shuffle_agent.png",
|
||||
scrollbarColor: resolvedMode === "dark" ? "#494949 #2f2f2f": "#c1c1c1 #f1f1f1",
|
||||
scrollbarColorTransparent: resolvedMode === "dark" ? '#494949 transparent': "#c1c1c1 transparent",
|
||||
},
|
||||
typography: {
|
||||
fontFamily: `"inter", "Roboto", "Helvetica", "Arial", sans-serif`,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#000000",
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#000000",
|
||||
useNextVariants: true,
|
||||
fontWeightLight: 300,
|
||||
fontWeightRegular: 400,
|
||||
@@ -272,36 +292,36 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
fontWeightSemiBold: 600,
|
||||
fontWeightBold: 700,
|
||||
allVariants: {
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A",
|
||||
},
|
||||
h1: {
|
||||
fontSize: 40,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
},
|
||||
h2: {
|
||||
fontSize: 36,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
},
|
||||
h3: {
|
||||
fontSize: 32,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
},
|
||||
h4: {
|
||||
fontSize: 30,
|
||||
fontWeight: 500,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
},
|
||||
h6: {
|
||||
fontSize: 22,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
},
|
||||
body1: {
|
||||
fontSize: 16,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
},
|
||||
body2: {
|
||||
fontSize: 14,
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A"
|
||||
},
|
||||
},
|
||||
components: {
|
||||
@@ -316,7 +336,7 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
{
|
||||
props: { variant: 'text', color: 'primary' },
|
||||
style: {
|
||||
color: themeMode === "dark" ? "#ffffff" : "#1A1A1A",
|
||||
color: resolvedMode === "dark" ? "#ffffff" : "#1A1A1A",
|
||||
whiteSpace: "nowrap",
|
||||
textWrap: "normal",
|
||||
},
|
||||
@@ -324,7 +344,7 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
{
|
||||
props: { variant: 'text', color: 'secondary' },
|
||||
style: {
|
||||
color: themeMode === "dark" ? "#9E9E9E" : "#616161",
|
||||
color: resolvedMode === "dark" ? "#9E9E9E" : "#616161",
|
||||
whiteSpace: "nowrap",
|
||||
textWrap: "normal",
|
||||
},
|
||||
@@ -332,46 +352,48 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
{
|
||||
props: { variant: 'contained', color: 'primary' },
|
||||
style: {
|
||||
backgroundColor: themeMode === "dark" ? brandColor || '#ff8544' : brandColor || '#FF7C35',
|
||||
color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF',
|
||||
backgroundColor: resolvedMode === "dark" ? brandColor || '#ff8544' : brandColor || '#FF7C35',
|
||||
color: resolvedMode === "dark" ? '#1a1a1a': '#FFFFFF',
|
||||
borderRadius: '4px',
|
||||
whiteSpace: "nowrap",
|
||||
textWrap: "normal",
|
||||
transition: 'background-color 0.2s ease-in-out',
|
||||
'&:hover': {
|
||||
fontWeight: 600,
|
||||
backgroundColor: themeMode === 'dark' ? brandColor || "#ff955c" : brandColor || '#FF8D4F',
|
||||
color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF',
|
||||
backgroundColor: resolvedMode === 'dark' ? brandColor || "#ff955c" : brandColor || '#FF8D4F',
|
||||
color: resolvedMode === "dark" ? '#1a1a1a': '#FFFFFF',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
props: { variant: 'contained', color: 'secondary' },
|
||||
style: {
|
||||
backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
color: themeMode === "dark" ? '#ffffff' : '#4C4C4C',
|
||||
backgroundColor: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
color: resolvedMode === "dark" ? '#ffffff' : '#4C4C4C',
|
||||
borderRadius: '4px',
|
||||
boxShadow: 'none',
|
||||
whiteSpace: "nowrap",
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
textWrap: "normal",
|
||||
'&:hover': {
|
||||
fontWeight: 600,
|
||||
border: themeMode === "dark" ? '1px solid #f1f1f1' : 'none',
|
||||
backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
color: themeMode === "dark" ? '#ffffff' : '#4C4C4C',
|
||||
border: resolvedMode === "dark" ? '1px solid #f1f1f1' : 'none',
|
||||
backgroundColor: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
color: resolvedMode === "dark" ? '#ffffff' : '#4C4C4C',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
props: { variant: 'outlined', color: 'primary' },
|
||||
style: {
|
||||
borderColor: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
|
||||
color: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
|
||||
borderColor: resolvedMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
|
||||
color: resolvedMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f",
|
||||
whiteSpace: "nowrap",
|
||||
fontWeight: 'normal',
|
||||
textWrap: "normal",
|
||||
'&:hover': {
|
||||
backgroundColor: themeMode === "dark" ? brandColor || "#ff8544" : "#ffe8dc",
|
||||
color: themeMode === "dark" ? "#1a1a1a" : "#8a3d00",
|
||||
backgroundColor: resolvedMode === "dark" ? brandColor || "#ff8544" : "#ffe8dc",
|
||||
color: resolvedMode === "dark" ? "#1a1a1a" : "#8a3d00",
|
||||
fontWeight: 600,
|
||||
},
|
||||
},
|
||||
@@ -380,14 +402,14 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
props: { variant: 'outlined', color: 'secondary' },
|
||||
style: {
|
||||
border: '1px solid #C5C5C5',
|
||||
color: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D',
|
||||
color: resolvedMode === "dark" ? '#C5C5C5' : '#2D2D2D',
|
||||
whiteSpace: "nowrap",
|
||||
textWrap: "normal",
|
||||
'&:hover': {
|
||||
backgroundColor: themeMode === "dark" ? '#C5C5C5' : '#EFEFEF',
|
||||
borderColor: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D',
|
||||
backgroundColor: resolvedMode === "dark" ? '#C5C5C5' : '#EFEFEF',
|
||||
borderColor: resolvedMode === "dark" ? '#C5C5C5' : '#2D2D2D',
|
||||
fontWeight: 600,
|
||||
color: themeMode === "dark" ? '#1a1a1a' : '#1A1A1A',
|
||||
color: resolvedMode === "dark" ? '#1a1a1a' : '#1A1A1A',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -410,8 +432,8 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
background: 'linear-gradient(90deg, #e6743a 0%, #d4456e 50%, #8a4de8 100%)',
|
||||
},
|
||||
'&:disabled': {
|
||||
background: themeMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
color: themeMode === "dark" ? '#9E9E9E' : '#616161',
|
||||
background: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
color: resolvedMode === "dark" ? '#9E9E9E' : '#616161',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -456,9 +478,9 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
},
|
||||
'&:disabled': {
|
||||
background: 'transparent',
|
||||
color: themeMode === "dark" ? '#9E9E9E' : '#616161',
|
||||
color: resolvedMode === "dark" ? '#9E9E9E' : '#616161',
|
||||
'&::before': {
|
||||
background: themeMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
background: resolvedMode === "dark" ? '#494949' : '#C9C9C9',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -468,7 +490,7 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
MuiTab: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: themeMode === "dark" ? "#C5C5C5" : "#1A1A1A",
|
||||
color: resolvedMode === "dark" ? "#C5C5C5" : "#1A1A1A",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -477,7 +499,7 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
overrides: {
|
||||
MuiMenu: {
|
||||
list: {
|
||||
backgroundColor: themeMode === "dark" ? "#27292d" : "#ffffff",
|
||||
backgroundColor: resolvedMode === "dark" ? "#27292d" : "#ffffff",
|
||||
},
|
||||
},
|
||||
MuiCssBaseline: {
|
||||
@@ -523,3 +545,5 @@ export const getTheme = (themeMode, brandColor) =>
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3363,7 +3363,6 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
color="primary"
|
||||
label={"Edit value"}
|
||||
defaultValue={props.data.limit}
|
||||
style={{}}
|
||||
onChange={(event) => {
|
||||
setNewValue(event.target.value);
|
||||
}}
|
||||
@@ -4084,7 +4083,6 @@ If you're interested, please let me know a time that works for you, or set up a
|
||||
selectedOrganization={selectedOrganization}
|
||||
adminTab={adminTab}
|
||||
billingInfo={billingInfo}
|
||||
selectedOrganization={selectedOrganization}
|
||||
stripeKey={props.stripeKey}
|
||||
handleGetOrg={handleGetOrg}
|
||||
/>
|
||||
@@ -6822,7 +6820,6 @@ curTab === 6 ? (
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
padding: 15,
|
||||
textAlign: "center",
|
||||
height: 70,
|
||||
textAlign: "left",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
|
||||
@@ -475,7 +475,7 @@ const AgentUI = (props) => {
|
||||
getAppAuth()
|
||||
}, [])
|
||||
|
||||
const maxTimelineWidth = 380
|
||||
const maxTimelineWidth = 375
|
||||
|
||||
const submitQuestions = (decisionId, questionAnswers, isContinuation) => {
|
||||
console.log("Submitting questions: ", decisionId, questionAnswers)
|
||||
@@ -504,6 +504,13 @@ const AgentUI = (props) => {
|
||||
|
||||
} else {
|
||||
for (var key in questionAnswers) {
|
||||
// Special handler for continuation of normal actions
|
||||
// Approves/denies continuation
|
||||
if (key === "approve") {
|
||||
newArgument[key] = questionAnswers[key]
|
||||
break
|
||||
}
|
||||
|
||||
const answer = questionAnswers[key]
|
||||
newArgument["question_"+(answer.index)] = answer.value
|
||||
}
|
||||
@@ -643,6 +650,7 @@ const AgentUI = (props) => {
|
||||
item.label = item?.details?.reason || item.label
|
||||
|
||||
} else if (item?.category === "ask" || item?.details?.action === "ask") {
|
||||
console.log("IN HERE?: ", item)
|
||||
|
||||
item.type = "question"
|
||||
item.category = "ask"
|
||||
@@ -663,6 +671,8 @@ const AgentUI = (props) => {
|
||||
item.label = item?.details?.reason || item.label
|
||||
}
|
||||
|
||||
console.log("ITEM: ", item, questions)
|
||||
|
||||
var parsedCategory = item.category === "singul" ?
|
||||
<Tooltip title="Singul" placement="top">
|
||||
<img src="/images/logos/singul.svg" style={categoryStyle} />
|
||||
@@ -683,7 +693,14 @@ const AgentUI = (props) => {
|
||||
if (item?.details?.tool !== undefined && item?.details?.tool !== null && item?.details?.tool?.length > 0 && item?.details?.tool !== "singul" && item?.details?.tool !== item?.details?.action) {
|
||||
|
||||
// Find the app and inject the image
|
||||
const toolName = item.details.tool.toLowerCase().replaceAll(" ", "_").replaceAll("-", "_")
|
||||
var toolName = item?.details?.tool?.toLowerCase().replaceAll(" ", "_").replaceAll("-", "_")
|
||||
if (toolName.includes("app:")) {
|
||||
const toolNameSplit = toolName.split(":")
|
||||
if (toolNameSplit.length >= 2) {
|
||||
toolName = toolNameSplit[2]
|
||||
}
|
||||
}
|
||||
|
||||
for (var appKey in apps) {
|
||||
const app = apps[appKey]
|
||||
|
||||
@@ -748,10 +765,17 @@ const AgentUI = (props) => {
|
||||
if (item?.details === undefined || item?.details === null || item?.details?.input === undefined || item?.details?.input === null) {
|
||||
toast.error("No decision details found to rerun. Cannot proceed. Please go back to your workflow or /agents to start over.")
|
||||
} else {
|
||||
if (item?.details?.original_input !== undefined && item?.details?.original_input !== null && item?.details?.original_input !== "") {
|
||||
setActionInput(item?.details?.original_input)
|
||||
setDisableButtons(true)
|
||||
submitInput(item?.details?.original_input)
|
||||
return
|
||||
}
|
||||
|
||||
//console.log("DETAILS: ", item?.details)
|
||||
for (var messagekey in item?.details?.input?.messages) {
|
||||
const message = item?.details?.input?.messages[messagekey]
|
||||
if (message.role === "user") {
|
||||
if (message.role === "user" && !message?.role?.includes("USER CONTEXT")) {
|
||||
setActionInput(message.content)
|
||||
setDisableButtons(true)
|
||||
|
||||
@@ -871,6 +895,9 @@ const AgentUI = (props) => {
|
||||
<div style={{
|
||||
minWidth: 300,
|
||||
maxWidth: 300,
|
||||
maxHeight: 150,
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
paddingTop: defaultTopPadding,
|
||||
paddingBottom: defaultTopPadding,
|
||||
}}>
|
||||
@@ -894,6 +921,7 @@ const AgentUI = (props) => {
|
||||
minWidth: maxTimelineWidth,
|
||||
maxWidth: maxTimelineWidth,
|
||||
paddingTop: defaultTopPadding*1.5,
|
||||
marginLeft: 5,
|
||||
}}>
|
||||
{currentDuration != 0 && !isNaN(timelineMarginLeft) && !isNaN(timelineWidth) && timelineWidth > 0 ?
|
||||
<div style={{
|
||||
@@ -921,40 +949,6 @@ const AgentUI = (props) => {
|
||||
{item.category === "ask" ?
|
||||
<span style={{display: "flex", }}>
|
||||
{rerunButton}
|
||||
{/*
|
||||
<Tooltip title="Approve" placement="left">
|
||||
<span>
|
||||
<IconButton
|
||||
disabled={disableButtons}
|
||||
style={{marginLeft: 20, }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
toast.info("Approving this step.")
|
||||
}}
|
||||
>
|
||||
<CheckIcon style={{color: green, }} />
|
||||
</IconButton>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip title="Deny" placement="left">
|
||||
<span>
|
||||
<IconButton
|
||||
disabled={item.type !== "decision" || disableButtons}
|
||||
style={{marginLeft: 0, }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
toast.info("Stopping on this step.")
|
||||
}}
|
||||
>
|
||||
<CloseIcon style={{color: red, }} />
|
||||
</IconButton>
|
||||
</span>
|
||||
</Tooltip>
|
||||
*/}
|
||||
|
||||
<Tooltip title="Answer in the Form UI" placement="left">
|
||||
<span>
|
||||
@@ -980,6 +974,60 @@ const AgentUI = (props) => {
|
||||
rerunAgentButton
|
||||
:
|
||||
item?.type === "decision" ?
|
||||
item?.details?.run_details?.status === "WAITING" ?
|
||||
agentRequestLoading ? null :
|
||||
<div>
|
||||
<Button
|
||||
style={{
|
||||
maxHeight: 30,
|
||||
minHeight: 30,
|
||||
minWidth: 75,
|
||||
maxWidth: 75,
|
||||
marginTop: 5,
|
||||
}}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
// FIXME: How do we approve it? Hmm
|
||||
toast.info("Approving this step.")
|
||||
|
||||
// Keeping it as string to make it simple
|
||||
const newAnswers = {
|
||||
"approve": "true",
|
||||
}
|
||||
submitQuestions(item?.details?.run_details?.id, newAnswers)
|
||||
}}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
style={{
|
||||
maxHeight: 30,
|
||||
minHeight: 30,
|
||||
minWidth: 75,
|
||||
maxWidth: 75,
|
||||
marginTop: 3,
|
||||
}}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
// Keeping it as string to make it simple
|
||||
const newAnswers = {
|
||||
"approve": "false",
|
||||
}
|
||||
submitQuestions(item?.details?.run_details?.id, newAnswers)
|
||||
}}
|
||||
>
|
||||
Deny
|
||||
</Button>
|
||||
</div>
|
||||
:
|
||||
<div style={{display: "flex", }}>
|
||||
{rerunButton}
|
||||
<Tooltip title="Explore/debug app run" placement="left">
|
||||
@@ -1025,7 +1073,7 @@ const AgentUI = (props) => {
|
||||
</div>
|
||||
|
||||
{showAuthentication && selectedApp.id !== undefined ?
|
||||
<div style={{minWidth: 300, maxWidth: 300, margin: "auto", marginTop: 25, }}>
|
||||
<div style={{minWidth: 300, maxWidth: 300, margin: "auto", marginTop: 0, marginBottom: 25, }}>
|
||||
<AuthenticationModal
|
||||
globalUrl={globalUrl}
|
||||
userdata={userdata}
|
||||
@@ -1036,7 +1084,7 @@ const AgentUI = (props) => {
|
||||
</div>
|
||||
: null}
|
||||
|
||||
{questions?.length > 0 && item?.status === "RUNNING" ?
|
||||
{questions?.length > 0 && item?.status === "RUNNING" || item?.status === "WAITING" ?
|
||||
<div>
|
||||
{questions.map((q, questionIndex) => {
|
||||
return (
|
||||
@@ -1094,7 +1142,7 @@ const AgentUI = (props) => {
|
||||
}}
|
||||
>
|
||||
{agentRequestLoading ?
|
||||
<CircularProgress size={20} style={{marginRight: 10, }} />
|
||||
<CircularProgress size={20} style={{marginRight: 3, }} />
|
||||
:
|
||||
"Submit"
|
||||
}
|
||||
@@ -1259,6 +1307,7 @@ const AgentUI = (props) => {
|
||||
}
|
||||
|
||||
if (nextItem.start_time - currentItem.end_time > 1) {
|
||||
/*
|
||||
timelineItems?.push({
|
||||
label: "",
|
||||
type: "processing",
|
||||
@@ -1267,6 +1316,7 @@ const AgentUI = (props) => {
|
||||
start_time: currentItem.end_time,
|
||||
end_time: nextItem.start_time,
|
||||
})
|
||||
*/
|
||||
}
|
||||
}
|
||||
timelineItems.sort((a, b) => a.start_time - b.start_time)
|
||||
@@ -1333,7 +1383,7 @@ const AgentUI = (props) => {
|
||||
}}
|
||||
>
|
||||
{finishAnswer !== "" ?
|
||||
<div style={{marginTop: 50, textAlign: "left", }}>
|
||||
<div style={{marginTop: 50, overflowX: "hidden", textAlign: "left", }}>
|
||||
<Markdown
|
||||
components={markdownComponents}
|
||||
id="markdown_wrapper"
|
||||
@@ -1586,6 +1636,14 @@ const AgentUI = (props) => {
|
||||
|
||||
<div />
|
||||
|
||||
<img
|
||||
src={theme.palette.singulBlackWhite}
|
||||
style={{
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h2" style={{marginTop: 30, }}>
|
||||
What do you want to do?
|
||||
</Typography>
|
||||
@@ -1593,14 +1651,18 @@ const AgentUI = (props) => {
|
||||
label="Get my emails for today and summarise them"
|
||||
variant="outlined"
|
||||
disabled={agentRequestLoading}
|
||||
style={{width: 500, marginTop: 30, }}
|
||||
style={{width: 650, marginTop: 30, }}
|
||||
multiline
|
||||
minRows={1}
|
||||
maxRows={20}
|
||||
defaultValue={actionInput || ""}
|
||||
onChange={(e) => {
|
||||
setActionInput(e.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
borderRadius: 30,
|
||||
},
|
||||
endAdornment: (
|
||||
agentRequestLoading ?
|
||||
<CircularProgress size={24} style={{marginRight: 10, }} />
|
||||
@@ -1616,8 +1678,8 @@ const AgentUI = (props) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<div style={{display: "flex", margin: "auto", paddingTop: 30, minWidth: 300, maxWidth: 300, justifyContent: "center", overflowWrap: "wrap", }}>
|
||||
<div>
|
||||
<div style={{display: "flex", margin: "auto", marginTop: 30, minWidth: 300, maxWidth: 300, justifyContent: "center", overflowWrap: "wrap", }}>
|
||||
<div style={{}}>
|
||||
<Chip
|
||||
id="add_app_chip"
|
||||
icon={<AddIcon />} label="Select Apps / MCPs"
|
||||
@@ -1642,14 +1704,19 @@ const AgentUI = (props) => {
|
||||
defaultSearch={""}
|
||||
newSelectedApp={newSelectedApp}
|
||||
setNewSelectedApp={setNewSelectedApp}
|
||||
inputHeight={200}
|
||||
inputHeight={250}
|
||||
apps={apps}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
{chosenApps.map((app, index) => {
|
||||
const chosenName = (app?.name?.charAt(0).toUpperCase() + app?.name?.slice(1))?.replaceAll("_", " ").replaceAll("-", " ")
|
||||
const chosenImagePath = app?.image
|
||||
var chosenName = (app?.name?.charAt(0).toUpperCase() + app?.name?.slice(1))?.replaceAll("_", " ").replaceAll("-", " ")
|
||||
if (chosenName === "API") {
|
||||
chosenName = "default"
|
||||
}
|
||||
|
||||
const chosenImagePath = app?.image === undefined || app?.image === null || app?.image === "" ? app?.large_image === undefined || app?.large_image === null || app?.large_image === "" ? theme.palette.singulBlackWhite : app.large_image : app.image
|
||||
const chosenImage = <img src={chosenImagePath} style={{width: 24, height: 24, borderRadius: 20, marginRight: 1, }} />
|
||||
|
||||
return (
|
||||
@@ -1731,7 +1798,7 @@ const AgentUI = (props) => {
|
||||
<Avatar
|
||||
key={index}
|
||||
alt={app?.name}
|
||||
src={app?.large_image}
|
||||
src={app?.large_image ? app.large_image : app?.image ? app.image : theme.palette.singulBlackWhite}
|
||||
onClick={() => {
|
||||
window.open(`/apps/${app.id}`, '_blank', 'noopener,noreferrer');
|
||||
}}
|
||||
|
||||
@@ -699,20 +699,20 @@ const AppCreator = (defaultprops) => {
|
||||
// This is the data FROM the database, not what's being saved
|
||||
const parseIncomingOpenapiData = (data) => {
|
||||
|
||||
var parsedDecoded = ""
|
||||
try {
|
||||
const decoded = base64_decode(data.openapi)
|
||||
parsedDecoded = decoded
|
||||
var parsedDecoded = ""
|
||||
try {
|
||||
const decoded = base64_decode(data.openapi)
|
||||
parsedDecoded = decoded
|
||||
} catch (e) {
|
||||
console.log("Failed JSON parsing: ", e)
|
||||
parsedDecoded = data
|
||||
}
|
||||
console.log("Failed JSON parsing: ", e)
|
||||
parsedDecoded = data
|
||||
}
|
||||
|
||||
if (data.openapi === null) {
|
||||
toast("Failed to load OpenAPI for app. Please contact support if this persists.")
|
||||
setIsAppLoaded(true);
|
||||
return
|
||||
}
|
||||
if (data.openapi === null) {
|
||||
toast("Failed to load OpenAPI for app. Please contact support if this persists.")
|
||||
setIsAppLoaded(true);
|
||||
return
|
||||
}
|
||||
|
||||
//console.log("Decoded: ", parsedDecoded)
|
||||
const parsedapp =
|
||||
@@ -916,36 +916,41 @@ const AppCreator = (defaultprops) => {
|
||||
body: "",
|
||||
errors: [],
|
||||
example_response: "",
|
||||
action_label: "No Label",
|
||||
action_label: ["No Label"],
|
||||
required_bodyfields: [],
|
||||
};
|
||||
|
||||
if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) {
|
||||
console.log("LABEL: ", methodvalue["x-label"])
|
||||
const labels = typeof methodvalue["x-label"] === 'object' || Array.isArray(methodvalue["x-label"]) ? methodvalue["x-label"] : (typeof methodvalue["x-label"] === 'string' ? methodvalue["x-label"].split(",") : [])
|
||||
|
||||
var correctlabel = ""
|
||||
const labels = methodvalue["x-label"].split(",")
|
||||
var correctlabels = []
|
||||
console.log("LABEL1: ", methodvalue["x-label"])
|
||||
for (let labelkey in labels) {
|
||||
var label = labels[labelkey].trim()
|
||||
if (label.toLowerCase() === "no label") {
|
||||
var label = labels[labelkey]?.trim()
|
||||
if (label?.toLowerCase() === "no label") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove quotes and escapes
|
||||
label = label.replace(/['"]+/g, '')
|
||||
label = label.replace(/\\/g, '')
|
||||
label = label?.replace(/['"]+/g, '')
|
||||
label = label?.replace(/\\/g, '')
|
||||
|
||||
//label = label.replace("_", " ", -1)
|
||||
//label = label.charAt(0).toUpperCase() + label.slice(1)
|
||||
// Uppercase first
|
||||
if (label === undefined || label === null || label?.length === 0 || label?.toLowerCase().replaceAll(" ", "_") === "no_label") {
|
||||
label = "No Label"
|
||||
}
|
||||
|
||||
correctlabel = label
|
||||
break
|
||||
label = label?.replace("_", " ")
|
||||
label = label?.charAt(0).toUpperCase() + label?.slice(1)
|
||||
|
||||
correctlabels?.push(label)
|
||||
}
|
||||
|
||||
console.log("LABEL: ", correctlabel)
|
||||
console.log("LABEL: ", correctlabels)
|
||||
|
||||
// FIX: Map labels only if they're actually in the category list
|
||||
//newaction.action_label = methodvalue["x-label"]
|
||||
newaction.action_label = correctlabel
|
||||
newaction.action_label = correctlabels
|
||||
}
|
||||
|
||||
if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) {
|
||||
@@ -1681,7 +1686,7 @@ const AppCreator = (defaultprops) => {
|
||||
}
|
||||
}
|
||||
|
||||
//newaction.action_label = "No Label"
|
||||
//newaction.action_label = ["No Label"]
|
||||
newActions.push(newaction);
|
||||
}
|
||||
}
|
||||
@@ -1964,9 +1969,9 @@ const AppCreator = (defaultprops) => {
|
||||
setProjectCategories(all_categories);
|
||||
|
||||
// Rearrange them by which has action_label
|
||||
const firstActions = newActions.filter(data => data.action_label !== undefined && data.action_label !== null && data.action_label !== "No Label")
|
||||
const firstActions = newActions.filter(data => data.action_label !== undefined && data.action_label !== null && data.action_label[0] !== "No Label")
|
||||
console.log("First actions: ", firstActions)
|
||||
const secondActions = newActions.filter(data => data.action_label === undefined || data.action_label === null || data.action_label === "No Label")
|
||||
const secondActions = newActions.filter(data => data.action_label === undefined || data.action_label === null || data.action_label[0] === "No Label")
|
||||
newActions = firstActions.concat(secondActions)
|
||||
setActions(newActions);
|
||||
//data.paths[item.url][item.method.toLowerCase()]["x-label"] = item.action_label
|
||||
@@ -2117,17 +2122,16 @@ const AppCreator = (defaultprops) => {
|
||||
},
|
||||
};
|
||||
|
||||
if (item.action_label !== undefined && item.action_label !== "" && item.action_label !== "No Label") {
|
||||
//console.log("Action label: ", item.action_label)
|
||||
data.paths[item.url][item.method.toLowerCase()]["x-label"] = item.action_label
|
||||
}
|
||||
if (item.action_label !== undefined && item.action_label !== "" && item.action_label[0] !== "No Label") {
|
||||
data.paths[item.url][item.method.toLowerCase()]["x-label"] = item.action_label
|
||||
}
|
||||
|
||||
if (item.required_bodyfields !== undefined && item.required_bodyfields !== null && item.required_bodyfields.length > 0) {
|
||||
console.log("Required bodyfields: ", item.required_bodyfields)
|
||||
data.paths[item.url][item.method.toLowerCase()]["x-required-fields"] = item.required_bodyfields
|
||||
}
|
||||
if (item.required_bodyfields !== undefined && item.required_bodyfields !== null && item.required_bodyfields.length > 0) {
|
||||
console.log("Required bodyfields: ", item.required_bodyfields)
|
||||
data.paths[item.url][item.method.toLowerCase()]["x-required-fields"] = item.required_bodyfields
|
||||
}
|
||||
|
||||
//console.log("ACTION: ", item)
|
||||
//console.log("ACTION: ", item)
|
||||
|
||||
if (item.example_response !== undefined && item.example_response !== null && item.example_response.length > 0) {
|
||||
|
||||
@@ -3451,7 +3455,7 @@ const AppCreator = (defaultprops) => {
|
||||
errors: [],
|
||||
example_response: "",
|
||||
method: actionNonBodyRequest[0],
|
||||
action_label: "No Label",
|
||||
action_label: ["No Label"],
|
||||
required_bodyfields: [],
|
||||
});
|
||||
|
||||
@@ -4453,6 +4457,31 @@ const AppCreator = (defaultprops) => {
|
||||
const url = data.url;
|
||||
const hasFile = (data["file_field"] !== undefined && data["file_field"] !== null && data["file_field"].length > 0) || data["example_response"] === "shuffle_file_download"
|
||||
|
||||
if (data?.action_label[0] !== 'No Label') {
|
||||
console.log("Action label: ", data?.name, data?.action_label)
|
||||
}
|
||||
|
||||
var currentActionLabels = JSON.parse(JSON.stringify(actionLabels))
|
||||
for (var labelKey in data?.action_label) {
|
||||
const label = data?.action_label[labelKey]
|
||||
if (label.toLowerCase().replaceAll(" ", "_") === "no_label") {
|
||||
continue
|
||||
}
|
||||
|
||||
var found = false
|
||||
for (var acatKey in currentActionLabels) {
|
||||
const acat = currentActionLabels[acatKey]
|
||||
if (acat === label) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
console.log(`Not found: '${label}' in ${currentActionLabels}`)
|
||||
currentActionLabels.push(data?.action_label[labelKey])
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper key={index} style={actionListStyle}>
|
||||
@@ -4535,23 +4564,38 @@ const AppCreator = (defaultprops) => {
|
||||
*/}
|
||||
|
||||
{/* From 2023: Example of handling action labels */}
|
||||
{actionLabels.length > 0 && newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 && categories.length > 0 ?
|
||||
{currentActionLabels.length > 0 && newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 && categories.length > 0 ?
|
||||
<Select
|
||||
multiple={true}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
console.log("Should change: ", e.target.value, " Index: ", index)
|
||||
if (e.target.value === undefined || e.target.value === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (e.target.value.length === 0) {
|
||||
e.target.value = ["No Label"]
|
||||
}
|
||||
|
||||
const foundIndex = actions.findIndex((action) => action.name === data.name)
|
||||
console.log("Found index: ", foundIndex)
|
||||
if (foundIndex !== undefined && foundIndex !== null && foundIndex >= 0) {
|
||||
if (e.target.value.includes("no_label") || e.target.value.includes("No_Label") || e.target.value.includes("No Label")) {
|
||||
actions[foundIndex].action_label = ["No Label"]
|
||||
setActions(actions)
|
||||
setUpdate(Math.random())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Should change: ", e.target.value, " Index: ", index)
|
||||
actions[foundIndex].action_label = e.target.value
|
||||
setActions(actions)
|
||||
setUpdate(Math.random())
|
||||
}
|
||||
}}
|
||||
value={data?.action_label?.replace(" ", "_").toLowerCase()}
|
||||
value={data?.action_label}
|
||||
style={{
|
||||
border: data.action_label === undefined || data.action_label === "No Label" ? "" : `2px solid ${bgColor}`,
|
||||
border: data.action_label === undefined || data.action_label[0] === "No Label" ? "" : `2px solid ${bgColor}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
paddingLeft: 10,
|
||||
@@ -4566,12 +4610,15 @@ const AppCreator = (defaultprops) => {
|
||||
name: "Method",
|
||||
id: "method-option",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
console.log("Current labels: ", data?.action_label)
|
||||
}}
|
||||
>
|
||||
{actionLabels.map((label, labelindex) => {
|
||||
{currentActionLabels.map((label, labelindex) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={labelindex}
|
||||
value={label.replace(" ", "_").toLowerCase()}
|
||||
value={label}
|
||||
style={{
|
||||
}}
|
||||
>
|
||||
@@ -4636,7 +4683,7 @@ const AppCreator = (defaultprops) => {
|
||||
body: "",
|
||||
errors: [],
|
||||
method: actionNonBodyRequest[0],
|
||||
action_label: "No Label",
|
||||
action_label: ["No Label"],
|
||||
required_bodyfields: [],
|
||||
});
|
||||
setActionsModalOpen(true);
|
||||
@@ -5388,15 +5435,15 @@ const AppCreator = (defaultprops) => {
|
||||
body: "",
|
||||
errors: [],
|
||||
method: actionNonBodyRequest[0],
|
||||
action_label: "No Label",
|
||||
required_bodyfields: [],
|
||||
action_label: ["No Label"],
|
||||
required_bodyfields: [],
|
||||
});
|
||||
|
||||
console.log("Added: ", actions)
|
||||
console.log("Added: ", actions)
|
||||
setActions(actions)
|
||||
setFilteredActions(actions)
|
||||
setFilteredActions(actions)
|
||||
setActionAmount(50);
|
||||
setUpdate(Math.random());
|
||||
setUpdate(Math.random());
|
||||
}}
|
||||
>
|
||||
New action
|
||||
@@ -5420,7 +5467,7 @@ const AppCreator = (defaultprops) => {
|
||||
// body: "",
|
||||
// errors: [],
|
||||
// method: actionNonBodyRequest[0],
|
||||
// action_label: "No Label",
|
||||
// action_label: ["No Label"],
|
||||
// required_bodyfields: [],
|
||||
//})
|
||||
//setActions(actions)
|
||||
|
||||
@@ -73,14 +73,14 @@ import {
|
||||
Index,
|
||||
} from "react-instantsearch-dom";
|
||||
import AppStats from "../components/AppStats.jsx";
|
||||
import ParsedAction from "../components/ParsedAction.jsx";
|
||||
import ParsedAction from "../components/ParsedActionNew.jsx";
|
||||
import { validateJson, GetIconInfo } from "../views/Workflows2.jsx";
|
||||
import { base64_decode, appCategories } from "../views/AppCreator.jsx";
|
||||
import { triggers as workflowTriggers } from "../views/AngularWorkflow.jsx";
|
||||
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||
import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
|
||||
import { CodeHandler, Img, OuterLink, CopyToClipboard, } from "../views/Docs.jsx";
|
||||
import { useStyles, } from "../components/ParsedAction.jsx";
|
||||
import { useStyles, } from "../components/ParsedActionNew.jsx";
|
||||
import { sortByKey } from "../views/AngularWorkflow.jsx";
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
@@ -241,6 +241,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
const [triggers, setTriggers] = useState([])
|
||||
const [selectedOrganization, setSelectedOrganization] = React.useState(undefined)
|
||||
const [selectedValidationAction, setSelectedValidationAction] = React.useState({})
|
||||
const [settingValidationAction, setSettingValidationAction] = React.useState(false)
|
||||
|
||||
const [selectedMeta, setSelectedMeta] = React.useState({
|
||||
link: "https://github.com/Shuffle/openapi-apps/new/master/docs",
|
||||
@@ -1165,8 +1166,8 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) {
|
||||
// Check if there are commas in it then loop and find the correct one
|
||||
// Should ignore 'No Label' and 'No label'
|
||||
var correctlabel = ""
|
||||
const labels = methodvalue["x-label"].split(",")
|
||||
const labels = typeof methodvalue["x-label"] === 'object' || Array.isArray(methodvalue["x-label"]) ? methodvalue["x-label"] : methodvalue["x-label"].split(",")
|
||||
var correctlabels = []
|
||||
for (let labelkey in labels) {
|
||||
var label = labels[labelkey].trim()
|
||||
if (label.toLowerCase() === "no label") {
|
||||
@@ -1180,12 +1181,14 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
//label = label.replace("_", " ", -1)
|
||||
//label = label.charAt(0).toUpperCase() + label.slice(1)
|
||||
|
||||
correctlabel = label
|
||||
break
|
||||
correctlabels.push(label)
|
||||
if (label === "app_validation") {
|
||||
setSelectedValidationAction(newaction)
|
||||
}
|
||||
}
|
||||
|
||||
// FIX: Map labels only if they're actually in the category list
|
||||
newaction.action_label = correctlabel
|
||||
newaction.action_label = correctlabels
|
||||
}
|
||||
|
||||
if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) {
|
||||
@@ -1450,11 +1453,12 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
const index = searchClient.initIndex("appsearch");
|
||||
|
||||
console.log("Running appsearch for: ", appname);
|
||||
if (appname === "integration") {
|
||||
if (appname === "integration" || appname == "singul") {
|
||||
// Redirect to https://singul.io
|
||||
window.location.href = "https://singul.io"
|
||||
} else if (appname === "shuffle_agent") {
|
||||
} else if (appname === "shuffle_agent" || appname === "agent" || appname === "agents" || appname == "shuffle-agent") {
|
||||
navigate("/agents")
|
||||
return
|
||||
}
|
||||
|
||||
index
|
||||
@@ -2933,7 +2937,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
}
|
||||
|
||||
const ActionSelectOption = (actionprops) => {
|
||||
const { option, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops;
|
||||
const { option, newActionname, newActiondescription, useIcon, extraDescription, selected, } = actionprops;
|
||||
const [hover, setHover] = React.useState(false);
|
||||
|
||||
return (
|
||||
@@ -2947,14 +2951,18 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
padding: 8,
|
||||
paddingLeft: 14,
|
||||
paddingBottom: 4,
|
||||
backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor,
|
||||
backgroundColor: selected === true ? theme.palette.platformColor : hover ? theme.palette.surfaceColor : theme.palette.inputColor,
|
||||
border: selected === true ? `2px solid ${theme.palette.platformColor}` : `2px solid ${theme.palette.inputColor}`,
|
||||
}} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
||||
onClick={(event) => {
|
||||
console.log("Clicked on action: ", option)
|
||||
|
||||
|
||||
if (option !== undefined && option !== null) {
|
||||
// FIXME: Add removal of app_validation from others
|
||||
setSelectedValidationAction(option)
|
||||
toast.info(`Selecting validation action...`)
|
||||
setSettingValidationAction(true)
|
||||
|
||||
const labelData = {
|
||||
"app_id": app.id,
|
||||
@@ -2975,12 +2983,20 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
|
||||
toast(responseJson.reason)
|
||||
setSettingValidationAction(false)
|
||||
|
||||
if (responseJson.success === true) {
|
||||
toast.success("Validation action selected")
|
||||
} else if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
|
||||
toast.error(responseJson.reason)
|
||||
} else {
|
||||
toast.error("Failed selecting validation action")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error: ", error)
|
||||
setSettingValidationAction(false)
|
||||
toast.error("Failed selecting validation action")
|
||||
})
|
||||
|
||||
// Update the app itself?
|
||||
@@ -3485,223 +3501,280 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
|
||||
The validation action is the action that is used to validate the app. This is used both when a user wants to validate their auth, as well as when Shuffle runs automatic tests of the app. It is recommended that the action should be a GET request. Validation is decided based on whether the action is ran successfully in a workflow.
|
||||
</Typography>
|
||||
|
||||
<Autocomplete
|
||||
id="action_search"
|
||||
value={selectedValidationAction}
|
||||
classes={{ inputRoot: classes.inputRoot }}
|
||||
groupBy={(option) => {
|
||||
// Most popular
|
||||
// Is categorized
|
||||
// Uncategorized
|
||||
return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions";
|
||||
}}
|
||||
renderGroup={(params) => {
|
||||
<div style={{display: "flex", }}>
|
||||
<Autocomplete
|
||||
id="action_search"
|
||||
value={selectedValidationAction}
|
||||
classes={{ inputRoot: classes.inputRoot }}
|
||||
groupBy={(option) => {
|
||||
// Most popular
|
||||
// Is categorized
|
||||
// Uncategorized
|
||||
return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions";
|
||||
}}
|
||||
renderGroup={(params) => {
|
||||
|
||||
return (
|
||||
<li key={params.key}>
|
||||
<Typography variant="body1" style={{textAlign: "center", marginLeft: 10, marginTop: 25, marginBottom: 10, }}>{params.group}</Typography>
|
||||
<Typography variant="body2">{params.children}</Typography>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
options={renderedActionOptions}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
filterOptions={(options, { inputValue }) => {
|
||||
const lowercaseValue = inputValue === null ? "" : inputValue.toLowerCase()
|
||||
options = options.filter((x) => {
|
||||
if (x.name === undefined || x.name === null) {
|
||||
x.name = ""
|
||||
}
|
||||
|
||||
if (x.description === undefined || x.description === null) {
|
||||
x.description = ""
|
||||
}
|
||||
|
||||
if (x.method !== "GET") {
|
||||
return null
|
||||
}
|
||||
|
||||
return x.name.replaceAll("_", " ").toLowerCase().includes(lowercaseValue) || x.description.toLowerCase().includes(lowercaseValue)
|
||||
})
|
||||
|
||||
return options
|
||||
}}
|
||||
getOptionLabel={(option) => {
|
||||
if (option === undefined || option === null || option.name === undefined || option.name === null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const newname = (
|
||||
option.name.charAt(0).toUpperCase() + option.name.substring(1)
|
||||
).replaceAll("_", " ");
|
||||
|
||||
return newname;
|
||||
}}
|
||||
fullWidth
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
// Workaround with event lol
|
||||
console.log("Changed to: ", event, newValue)
|
||||
toast("Changed validation action")
|
||||
if (newValue !== undefined && newValue !== null) {
|
||||
/*
|
||||
setNewSelectedAction({
|
||||
target: {
|
||||
value: newValue.name
|
||||
}
|
||||
})
|
||||
*/
|
||||
}
|
||||
}}
|
||||
renderOption={(props, option, state) => {
|
||||
var newActionname = option.name;
|
||||
if (option.label !== undefined && option.label !== null && option.label.length > 0) {
|
||||
newActionname = option.label;
|
||||
}
|
||||
|
||||
var newActiondescription = option.description;
|
||||
//console.log("DESC: ", newActiondescription)
|
||||
if (option.description === undefined || option.description === null) {
|
||||
newActiondescription = "Description: No description defined for this action"
|
||||
} else {
|
||||
newActiondescription = "Description: "+newActiondescription
|
||||
}
|
||||
|
||||
const iconInfo = GetIconInfo({ name: option.name });
|
||||
const useIcon = iconInfo.originalIcon;
|
||||
|
||||
if (newActionname === undefined || newActionname === null) {
|
||||
newActionname = "No name"
|
||||
option.name = "No name"
|
||||
option.label = "No name"
|
||||
}
|
||||
|
||||
newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " ");
|
||||
|
||||
var method = ""
|
||||
var extraDescription = ""
|
||||
if (option.name.includes("get_")) {
|
||||
method = "GET"
|
||||
} else if (option.name.includes("post_")) {
|
||||
method = "POST"
|
||||
} else if (option.name.includes("put_")) {
|
||||
method = "PUT"
|
||||
} else if (option.name.includes("patch_")) {
|
||||
method = "PATCH"
|
||||
} else if (option.name.includes("delete_")) {
|
||||
method = "DELETE"
|
||||
} else if (option.name.includes("options_")) {
|
||||
method = "OPTIONS"
|
||||
} else if (option.name.includes("connect_")) {
|
||||
method = "CONNECT"
|
||||
}
|
||||
|
||||
// FIXME: Should it require a base URL?
|
||||
if (method.length > 0 && option.description !== undefined && option.description !== null && option.description.includes("http")) {
|
||||
var extraUrl = ""
|
||||
const descSplit = option.description.split("\n")
|
||||
// Last line of descSplit
|
||||
if (descSplit.length > 0) {
|
||||
extraUrl = descSplit[descSplit.length-1]
|
||||
}
|
||||
|
||||
if (extraUrl.length > 0) {
|
||||
if (extraUrl.includes(" ")) {
|
||||
extraUrl = extraUrl.split(" ")[0]
|
||||
return (
|
||||
<li key={params.key}>
|
||||
<Typography variant="body1" style={{textAlign: "center", marginLeft: 10, marginTop: 25, marginBottom: 10, }}>{params.group}</Typography>
|
||||
<Typography variant="body2">{params.children}</Typography>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
options={renderedActionOptions}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
filterOptions={(options, { inputValue }) => {
|
||||
const lowercaseValue = inputValue === null ? "" : inputValue.toLowerCase()
|
||||
options = options.filter((x) => {
|
||||
if (x.name === undefined || x.name === null) {
|
||||
x.name = ""
|
||||
}
|
||||
|
||||
if (extraUrl.includes("#")) {
|
||||
extraUrl = extraUrl.split("#")[0]
|
||||
if (x.description === undefined || x.description === null) {
|
||||
x.description = ""
|
||||
}
|
||||
|
||||
extraDescription = `${method} ${extraUrl}`
|
||||
} else {
|
||||
//console.log("No url found. Check again :)")
|
||||
}
|
||||
}
|
||||
if (x.method !== "GET") {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionSelectOption
|
||||
{...props}
|
||||
option={option}
|
||||
newActiondescription={newActiondescription}
|
||||
useIcon={useIcon}
|
||||
newActionname={newActionname}
|
||||
extraDescription={extraDescription}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
renderInput={(params) => {
|
||||
if (params.inputProps?.value) {
|
||||
const prefixes = ["Post", "Put", "Patch"];
|
||||
for (let prefix of prefixes) {
|
||||
if (params.inputProps.value.startsWith(prefix)) {
|
||||
let newValue = params.inputProps.value.replace(prefix + " ", "");
|
||||
if (newValue.length > 1) {
|
||||
newValue = newValue.charAt(0).toUpperCase() + newValue.substring(1);
|
||||
return x.name.replaceAll("_", " ").toLowerCase().includes(lowercaseValue) || x.description.toLowerCase().includes(lowercaseValue)
|
||||
})
|
||||
|
||||
return options
|
||||
}}
|
||||
getOptionLabel={(option) => {
|
||||
if (option === undefined || option === null || option.name === undefined || option.name === null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const newname = (
|
||||
option.name.charAt(0).toUpperCase() + option.name.substring(1)
|
||||
).replaceAll("_", " ");
|
||||
|
||||
return newname;
|
||||
}}
|
||||
fullWidth
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
// Workaround with event lol
|
||||
console.log("Changed to: ", event, newValue)
|
||||
toast("Changed validation action")
|
||||
if (newValue !== undefined && newValue !== null) {
|
||||
/*
|
||||
setNewSelectedAction({
|
||||
target: {
|
||||
value: newValue.name
|
||||
}
|
||||
})
|
||||
*/
|
||||
}
|
||||
}}
|
||||
renderOption={(props, option, state) => {
|
||||
var newActionname = option.name;
|
||||
if (option.label !== undefined && option.label !== null && option.label.length > 0) {
|
||||
newActionname = option.label;
|
||||
}
|
||||
|
||||
var newActiondescription = option.description;
|
||||
//console.log("DESC: ", newActiondescription)
|
||||
if (option.description === undefined || option.description === null) {
|
||||
newActiondescription = "Description: No description defined for this action"
|
||||
} else {
|
||||
newActiondescription = "Description: "+newActiondescription
|
||||
}
|
||||
|
||||
const iconInfo = GetIconInfo({ name: option.name });
|
||||
const useIcon = iconInfo.originalIcon;
|
||||
|
||||
if (newActionname === undefined || newActionname === null) {
|
||||
newActionname = "No name"
|
||||
option.name = "No name"
|
||||
option.label = "No name"
|
||||
}
|
||||
|
||||
newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " ");
|
||||
|
||||
var method = ""
|
||||
var extraDescription = ""
|
||||
if (option.name.includes("get_")) {
|
||||
method = "GET"
|
||||
} else if (option.name.includes("post_")) {
|
||||
method = "POST"
|
||||
} else if (option.name.includes("put_")) {
|
||||
method = "PUT"
|
||||
} else if (option.name.includes("patch_")) {
|
||||
method = "PATCH"
|
||||
} else if (option.name.includes("delete_")) {
|
||||
method = "DELETE"
|
||||
} else if (option.name.includes("options_")) {
|
||||
method = "OPTIONS"
|
||||
} else if (option.name.includes("connect_")) {
|
||||
method = "CONNECT"
|
||||
}
|
||||
|
||||
// FIXME: Should it require a base URL?
|
||||
if (method.length > 0 && option.description !== undefined && option.description !== null && option.description.includes("http")) {
|
||||
var extraUrl = ""
|
||||
const descSplit = option.description.split("\n")
|
||||
// Last line of descSplit
|
||||
if (descSplit.length > 0) {
|
||||
extraUrl = descSplit[descSplit.length-1]
|
||||
}
|
||||
|
||||
if (extraUrl.length > 0) {
|
||||
if (extraUrl.includes(" ")) {
|
||||
extraUrl = extraUrl.split(" ")[0]
|
||||
}
|
||||
// Set the new value without mutating inputProps
|
||||
params = { ...params, inputProps: { ...params.inputProps, value: newValue } };
|
||||
break;
|
||||
|
||||
if (extraUrl.includes("#")) {
|
||||
extraUrl = extraUrl.split("#")[0]
|
||||
}
|
||||
|
||||
extraDescription = `${method} ${extraUrl}`
|
||||
} else {
|
||||
//console.log("No url found. Check again :)")
|
||||
}
|
||||
}
|
||||
// Check if it starts with "Get List" and method is "Get"
|
||||
if (params.inputProps.value.startsWith("Get List")) {
|
||||
console.log("Get List")
|
||||
}
|
||||
}
|
||||
|
||||
const actionDescription = ""
|
||||
const isIntegration = false
|
||||
const isSelected = selectedValidationAction !== undefined && selectedValidationAction !== null && selectedValidationAction?.name === option.name
|
||||
|
||||
return (
|
||||
<Tooltip title={actionDescription}
|
||||
placement="right"
|
||||
open={false}
|
||||
PopperProps={{
|
||||
sx: {
|
||||
'& .MuiTooltip-tooltip': {
|
||||
backgroundColor: 'transparent',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
'& .MuiTooltip-arrow': {
|
||||
color: 'transparent',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
{...params}
|
||||
<ActionSelectOption
|
||||
{...props}
|
||||
option={option}
|
||||
newActiondescription={newActiondescription}
|
||||
useIcon={useIcon}
|
||||
newActionname={newActionname}
|
||||
extraDescription={extraDescription}
|
||||
|
||||
data-lpignore="true"
|
||||
autocomplete="off"
|
||||
dataLPIgnore="true"
|
||||
autoComplete="off"
|
||||
|
||||
color="primary"
|
||||
id="checkbox-search"
|
||||
variant="body1"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
label={"Select Validation Action"}
|
||||
variant="outlined"
|
||||
name={`disable_autocomplete_${Math.random()}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
selected={isSelected}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
renderInput={(params) => {
|
||||
if (params.inputProps?.value) {
|
||||
const prefixes = ["Post", "Put", "Patch"];
|
||||
for (let prefix of prefixes) {
|
||||
if (params.inputProps.value.startsWith(prefix)) {
|
||||
let newValue = params.inputProps.value.replace(prefix + " ", "");
|
||||
if (newValue.length > 1) {
|
||||
newValue = newValue.charAt(0).toUpperCase() + newValue.substring(1);
|
||||
}
|
||||
// Set the new value without mutating inputProps
|
||||
params = { ...params, inputProps: { ...params.inputProps, value: newValue } };
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Check if it starts with "Get List" and method is "Get"
|
||||
if (params.inputProps.value.startsWith("Get List")) {
|
||||
console.log("Get List")
|
||||
}
|
||||
}
|
||||
|
||||
const actionDescription = ""
|
||||
const isIntegration = false
|
||||
|
||||
return (
|
||||
<Tooltip title={actionDescription}
|
||||
placement="right"
|
||||
open={false}
|
||||
PopperProps={{
|
||||
sx: {
|
||||
'& .MuiTooltip-tooltip': {
|
||||
backgroundColor: 'transparent',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
'& .MuiTooltip-arrow': {
|
||||
color: 'transparent',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
{...params}
|
||||
|
||||
data-lpignore="true"
|
||||
autocomplete="off"
|
||||
dataLPIgnore="true"
|
||||
autoComplete="off"
|
||||
|
||||
color="primary"
|
||||
id="checkbox-search"
|
||||
variant="body1"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
label={"Select Validation Action"}
|
||||
variant="outlined"
|
||||
name={`disable_autocomplete_${Math.random()}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
disabled={selectedValidationAction === null || selectedValidationAction === undefined || executing || settingValidationAction}
|
||||
onClick={() => {
|
||||
if (app === null || app === undefined || app.id === undefined || app.name === undefined) {
|
||||
toast.error("App not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if validation action is selected
|
||||
if (selectedValidationAction === null || selectedValidationAction === undefined) {
|
||||
toast.error("Please select a validation action first");
|
||||
return;
|
||||
}
|
||||
|
||||
setExecuting(true);
|
||||
fetch(`${globalUrl}/api/v1/apps/${app.id}/test`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === true) {
|
||||
toast.success("Test executed successfully");
|
||||
} else {
|
||||
if (responseJson.reason && responseJson.reason.includes("No validation action found")) {
|
||||
toast.error("No validation action found. Please select a validation action from the dropdown above and try again.");
|
||||
} else {
|
||||
toast.error(responseJson.reason || "Test failed");
|
||||
}
|
||||
}
|
||||
setExecuting(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error:", error);
|
||||
toast.error("Request failed");
|
||||
setExecuting(false);
|
||||
});
|
||||
}}
|
||||
variant="outlined"
|
||||
style={{
|
||||
marginLeft: 20,
|
||||
}}
|
||||
>
|
||||
{executing ? "Testing..." : "Run Test"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Divider style={{marginTop: 30, marginBottom: 10, }}/>
|
||||
<Typography variant="h6" style={{marginTop: 20, marginBottom: 10, }}>
|
||||
|
||||
@@ -70,11 +70,10 @@ const inputColor = "#383B40";
|
||||
|
||||
const chipStyle = {
|
||||
backgroundColor: "#3d3f43",
|
||||
height: 30,
|
||||
height: 28,
|
||||
marginRight: 5,
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
height: 28,
|
||||
cursor: "pointer",
|
||||
borderColor: "#3d3f43",
|
||||
color: "white",
|
||||
@@ -634,7 +633,7 @@ const Apps = (props) => {
|
||||
// dropdown with copy etc I guess
|
||||
const AppPaper = (props) => {
|
||||
const { app } = props
|
||||
const data = app
|
||||
let data = app
|
||||
|
||||
if (data.name === "" && data.id === "") {
|
||||
return null;
|
||||
@@ -802,7 +801,6 @@ const Apps = (props) => {
|
||||
justifyContent: "center",
|
||||
overflow: "hidden",
|
||||
maxHeight: 43,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
@@ -1165,7 +1163,6 @@ const Apps = (props) => {
|
||||
position: "absolute",
|
||||
top: -10,
|
||||
right: isCloud ? 50 : 0,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
backgroundColor: inputColor,
|
||||
color: "white",
|
||||
height: 35,
|
||||
@@ -1511,7 +1508,6 @@ const Apps = (props) => {
|
||||
borderRadius: 17 / 2,
|
||||
backgroundColor: itemColor,
|
||||
marginRight: 10,
|
||||
marginTop: 2,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
@@ -1945,7 +1941,7 @@ const Apps = (props) => {
|
||||
const baseImage = <LibraryBooksIcon />
|
||||
|
||||
return (
|
||||
<div style={{ position: "relative", marginTop: 15, marginLeft: 0, marginRight: 10, position: "absolute", color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, minWidth: leftBarSize - 10, maxWidth: leftBarSize - 10, boxShadows: "none", overflowX: "hidden", }}>
|
||||
<div style={{ position: "absolute", marginTop: 15, marginLeft: 0, marginRight: 10, color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, minWidth: leftBarSize - 10, maxWidth: leftBarSize - 10, boxShadows: "none", overflowX: "hidden", }}>
|
||||
<List style={{ backgroundColor: theme.palette.inputColor, }}>
|
||||
{hits.length === 0 ?
|
||||
<ListItem style={outerlistitemStyle}>
|
||||
|
||||
@@ -1360,6 +1360,13 @@ const Apps2 = (props) => {
|
||||
console.log("Failed to set apps in localstorage: ", e)
|
||||
}
|
||||
}
|
||||
|
||||
const userId = userdata?.id;
|
||||
if (userId !== undefined && userId !== null) {
|
||||
const filteredUserApps = privateapps.filter((app) => app.owner === userId);
|
||||
setUserApps(filteredUserApps);
|
||||
}
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Failed to get apps: ", error.toString());
|
||||
@@ -1368,6 +1375,14 @@ const Apps2 = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const userId = userdata?.id;
|
||||
if (userId !== undefined && userId !== null && userId.length > 0 && apps.length > 0) {
|
||||
const filteredUserApps = apps.filter((app) => app.owner === userId);
|
||||
setUserApps(filteredUserApps);
|
||||
}
|
||||
}, [userdata, apps]);
|
||||
|
||||
// Locally hotloads app from folder
|
||||
const hotloadApps = () => {
|
||||
toast("Hotloading apps from location in .env");
|
||||
|
||||
@@ -853,14 +853,14 @@ const UsecaseListComponent = (props) => {
|
||||
<li {...props}>
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||
<img src={data.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
}>
|
||||
<span>
|
||||
<Checkbox
|
||||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
|
||||
|
||||
@@ -1279,7 +1279,6 @@ const Docs = (defaultprops) => {
|
||||
overflow: "hidden",
|
||||
paddingBottom: 100,
|
||||
marginLeft: mobile ? 0 : 50,
|
||||
marginTop: 50,
|
||||
textAlign: "center",
|
||||
margin: "auto",
|
||||
marginTop: 50,
|
||||
|
||||
@@ -972,7 +972,7 @@ const GettingStarted = (props) => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
|
||||
var boxColor = "#FECC00";
|
||||
var boxColor = "#FFC633";
|
||||
if (data.is_valid) {
|
||||
boxColor = "#86c142";
|
||||
}
|
||||
@@ -1562,7 +1562,7 @@ const GettingStarted = (props) => {
|
||||
renderCell: (params) => {
|
||||
const data = params.row.record;
|
||||
|
||||
var boxColor = "#FECC00";
|
||||
var boxColor = "#FFC633";
|
||||
if (data.is_valid) {
|
||||
boxColor = "#86c142";
|
||||
}
|
||||
|
||||
@@ -812,7 +812,7 @@ const LoginPage = props => {
|
||||
width: "max-content",
|
||||
}}
|
||||
>
|
||||
<form onSubmit={onSubmit} style={{ margin: 15, width: isMobile ? "100%" : "360px", width: "max-content", overflow: "hidden", textAlign: "center", }}>
|
||||
<form onSubmit={onSubmit} style={{ margin: 15, width: "max-content", overflow: "hidden", textAlign: "center", }}>
|
||||
<img
|
||||
style={{
|
||||
height: isMobile ? 44 : 60,
|
||||
|
||||
@@ -23,8 +23,6 @@ import {
|
||||
TrendingUp as TrendingUpIcon,
|
||||
TrendingDown as TrendingDownIcon,
|
||||
TaskAlt as TaskAltIcon,
|
||||
SuccessFailed as SuccessFailedIcon,
|
||||
RunsOverTime as RunsOverTimeIcon,
|
||||
ErrorOutline as ErrorOutlineIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ const SetAuthentication = (props) => {
|
||||
// Set the orgsession to be a cookie for __session cookie
|
||||
|
||||
setTimeout(() => {
|
||||
document.cookie = "__session=" + orgsession + "; path=/; max-age=1800"; // Cookie expires in 30 min
|
||||
document.cookie = "__session=" + orgsession + "; path=/; max-age=1800; SameSite=Lax"; // Cookie expires in 30 min
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import WelcomeForm2 from "../components/WelcomeForm2.jsx";
|
||||
import AppFramework from "../components/AppFramework.jsx";
|
||||
import {isMobile} from "react-device-detect";
|
||||
import {
|
||||
ArrorForwardIos as ArrowForwardIosIcon,
|
||||
ArrowForwardIos as ArrowForwardIosIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import {
|
||||
|
||||
@@ -3407,7 +3407,7 @@ const Workflows = (props) => {
|
||||
{userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ?
|
||||
<div style={{
|
||||
border: "1px solid rgba(255,255,255,0.1)", borderRadius: theme.palette?.borderRadius, marginTop: 10,
|
||||
marginBottom: 10, padding: 15, textAlign: "center", height: 70, textAlign: "left", backgroundColor:
|
||||
marginBottom: 10, padding: 15, height: 70, textAlign: "left", backgroundColor:
|
||||
theme.palette.surfaceColor, display: "flex", maxHeight: "105px", minHeight: "110px"
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -103,6 +103,9 @@ import {
|
||||
Publish as PublishIcon,
|
||||
GetApp as GetAppIcon,
|
||||
Image as ImageIcon,
|
||||
Shield as ShieldIcon,
|
||||
Warning as WarningIcon,
|
||||
ChevronRight as ChevronRightIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
// Additional Components
|
||||
@@ -120,6 +123,7 @@ import { debounce } from "lodash";
|
||||
import { removeQuery } from "../components/ScrollToTop.jsx";
|
||||
|
||||
import {green, yellow, red, grey, triggers as wfTriggers, } from "../views/AngularWorkflow.jsx"
|
||||
import Licensed from "../components/Licensed.jsx";
|
||||
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e");
|
||||
@@ -255,6 +259,18 @@ export const handleReactJsonClipboard = (copy) => {
|
||||
// Takes an action in Shuffle and
|
||||
// Returns information about the icon, the color etc to be used
|
||||
// This can be used for actions of all types
|
||||
// PARSED ICON PROPERTIES:
|
||||
// icon - SVG <path> d attribute string
|
||||
// iconColor - path fill/stroke color (used in cytoscape node SVGs; "white" on colored bg)
|
||||
// iconBackgroundColor - solid bg color for the node
|
||||
// fillGradient - [color1, color2] for gradient bg; if set, fillstyle = "linear-gradient"
|
||||
// originalIcon - React <svg> element (22x22, currentColor) used in ParsedActionNew dropdown
|
||||
// svgViewBox - viewBox for the SVG; "0 0 48 48" for 48-coord paths, defaults to "0 0 24 24"
|
||||
// dropdownViewBox - tighter crop viewBox for dropdown rendering (auto: "10 10 28 28" for 48x48)
|
||||
// useStroke - true → render path with stroke (no fill); for outline-style icons
|
||||
// useFill - true + useStroke → render with BOTH fill and stroke
|
||||
// fillRule - "evenodd" for complex filled shapes (e.g. filter); default "nonzero"
|
||||
//
|
||||
export const GetIconInfo = (action) => {
|
||||
// Finds the icon based on the action. Should be verbs.
|
||||
const iconList = [
|
||||
@@ -263,6 +279,7 @@ export const GetIconInfo = (action) => {
|
||||
{ key: "cache_get", values: ["get_cache"] },
|
||||
{ key: "filter", values: ["filter"] },
|
||||
{ key: "merge", values: ["join", "merge", "route", "router"] },
|
||||
{key: "get_file_value", values: ["get_file_value"]},
|
||||
{
|
||||
key: "search",
|
||||
values: ["search", "find", "locate", "index", "analyze", "anal", "match", "check cache", "check", "verify", "validate", "siem", ],
|
||||
@@ -343,7 +360,7 @@ export const GetIconInfo = (action) => {
|
||||
"protect",
|
||||
],
|
||||
},
|
||||
{ key: "intel", values: ["intel", "feed", "threat intel", "threat intelligence", "ti", "t.i.", "t.i", "ti.", "rule", "technique", "tactic", "techniques", "tactics", "ioc", "indicator", "hash", "ip", "url", "domain", "sha", "md5", ] },
|
||||
{ key: "intel", values: ["intel", "feed", "threat intel", "threat intelligence", "ti", "t.i.", "t.i", "ti.", "rule", "technique", "tactic", "techniques", "tactics", "ioc", "indicator", "hash", "ip", "url", "domain", "sha", "md5",] },
|
||||
];
|
||||
|
||||
var selectedKey = ""
|
||||
@@ -392,57 +409,132 @@ export const GetIconInfo = (action) => {
|
||||
originalIcon: "",
|
||||
fillGradient: ["#8acc3f", "#459622"],
|
||||
},
|
||||
cache_add: {
|
||||
icon: "M11 3C6.58 3 3 4.79 3 7C3 9.21 6.58 11 11 11C15.42 11 19 9.21 19 7C19 4.79 15.42 3 11 3ZM3 9V12C3 14.21 6.58 16 11 16C15.42 16 19 14.21 19 12V9C19 11.21 15.42 13 11 13C6.58 13 3 11.21 3 9ZM3 14V17C3 19.21 6.58 21 11 21C12.41 21 13.79 20.81 15 20.46V17.46C13.79 17.81 12.41 18 11 18C6.58 18 3 16.21 3 14ZM20 14V17H17V19H20V22H22V19H25V17H22V14",
|
||||
// cache_add: {
|
||||
// icon: "M11 3C6.58 3 3 4.79 3 7C3 9.21 6.58 11 11 11C15.42 11 19 9.21 19 7C19 4.79 15.42 3 11 3ZM3 9V12C3 14.21 6.58 16 11 16C15.42 16 19 14.21 19 12V9C19 11.21 15.42 13 11 13C6.58 13 3 11.21 3 9ZM3 14V17C3 19.21 6.58 21 11 21C12.41 21 13.79 20.81 15 20.46V17.46C13.79 17.81 12.41 18 11 18C6.58 18 3 16.21 3 14ZM20 14V17H17V19H20V22H22V19H25V17H22V14",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: "#8acc3f",
|
||||
// originalIcon: "",
|
||||
// fillGradient: ["#8acc3f", "#459622"],
|
||||
// },
|
||||
get_file_value: {
|
||||
icon: "M25.8332 14.8335H18.4998C18.0136 14.8335 17.5473 15.0267 17.2035 15.3705C16.8597 15.7143 16.6665 16.1806 16.6665 16.6668V31.3335C16.6665 31.8197 16.8597 32.286 17.2035 32.6299C17.5473 32.9737 18.0136 33.1668 18.4998 33.1668H29.4998C29.9861 33.1668 30.4524 32.9737 30.7962 32.6299C31.14 32.286 31.3332 31.8197 31.3332 31.3335V20.3335L25.8332 14.8335Z M25.8335 14.8335V20.3335H31.3335 M24 29.5V24 M21.25 26.75H26.75",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "#8acc3f",
|
||||
iconBackgroundColor: "rgba(43, 189, 160, 1)",
|
||||
originalIcon: "",
|
||||
fillGradient: ["#8acc3f", "#459622"],
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
fillGradient: ["rgba(43, 189, 160, 1)", "rgba(43, 189, 160, 1)"],
|
||||
},
|
||||
cache_get: {
|
||||
icon: "M12 2C7.58 2 4 3.79 4 6C4 8.06 7.13 9.74 11.15 9.96C12.45 8.7 14.19 8 16 8C16.8 8 17.59 8.14 18.34 8.41C19.37 7.74 20 6.91 20 6C20 3.79 16.42 2 12 2ZM4 8V11C4 12.68 6.08 14.11 9 14.71C9.06 13.7 9.32 12.72 9.77 11.82C6.44 11.34 4 9.82 4 8ZM15.93 9.94C14.75 9.95 13.53 10.4 12.46 11.46C8.21 15.71 13.71 22.5 18.75 19.17L23.29 23.71L24.71 22.29L20.17 17.75C22.66 13.97 19.47 9.93 15.93 9.94ZM15.9 12C17.47 11.95 19 13.16 19 15C19 15.7956 18.6839 16.5587 18.1213 17.1213C17.5587 17.6839 16.7956 18 16 18C13.33 18 12 14.77 13.88 12.88C14.47 12.29 15.19 12 15.9 12ZM4 13V16C4 18.05 7.09 19.72 11.06 19.95C10.17 19.07 9.54 17.95 9.22 16.74C6.18 16.17 4 14.72 4 13Z",
|
||||
cache_add: {
|
||||
icon: "M24 20.6667C28.1421 20.6667 31.5 19.5475 31.5 18.1667C31.5 16.786 28.1421 15.6667 24 15.6667C19.8579 15.6667 16.5 16.786 16.5 18.1667C16.5 19.5475 19.8579 20.6667 24 20.6667Z M31.5 24C31.5 25.3833 28.1667 26.5 24 26.5C19.8333 26.5 16.5 25.3833 16.5 24 M16.5 18.1667V29.8334C16.5 31.2167 19.8333 32.3334 24 32.3334C28.1667 32.3334 31.5 31.2167 31.5 29.8334V18.1667 M32 35C34.2091 35 36 33.2091 36 31C36 28.7909 34.2091 27 32 27C29.7909 27 28 28.7909 28 31C28 33.2091 29.7909 35 32 35Z M32 29V33 M30 31H34",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "#8acc3f",
|
||||
iconBackgroundColor: "rgba(43, 189, 160, 1)",
|
||||
fillGradient: ["rgba(43, 189, 160, 1)", "rgba(43, 189, 160, 1)"],
|
||||
originalIcon: "",
|
||||
fillGradient: ["#8acc3f", "#459622"],
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
// cache_get: {
|
||||
// icon: "M12 2C7.58 2 4 3.79 4 6C4 8.06 7.13 9.74 11.15 9.96C12.45 8.7 14.19 8 16 8C16.8 8 17.59 8.14 18.34 8.41C19.37 7.74 20 6.91 20 6C20 3.79 16.42 2 12 2ZM4 8V11C4 12.68 6.08 14.11 9 14.71C9.06 13.7 9.32 12.72 9.77 11.82C6.44 11.34 4 9.82 4 8ZM15.93 9.94C14.75 9.95 13.53 10.4 12.46 11.46C8.21 15.71 13.71 22.5 18.75 19.17L23.29 23.71L24.71 22.29L20.17 17.75C22.66 13.97 19.47 9.93 15.93 9.94ZM15.9 12C17.47 11.95 19 13.16 19 15C19 15.7956 18.6839 16.5587 18.1213 17.1213C17.5587 17.6839 16.7956 18 16 18C13.33 18 12 14.77 13.88 12.88C14.47 12.29 15.19 12 15.9 12ZM4 13V16C4 18.05 7.09 19.72 11.06 19.95C10.17 19.07 9.54 17.95 9.22 16.74C6.18 16.17 4 14.72 4 13Z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: "#8acc3f",
|
||||
// originalIcon: "",
|
||||
// fillGradient: ["#8acc3f", "#459622"],
|
||||
// },
|
||||
cache_get: {
|
||||
icon: "M22.25 14C24.379 14 26.3377 14.2865 27.79 14.7705C28.5123 15.0113 29.1516 15.3144 29.625 15.6875C30.09 16.054 30.5 16.5766 30.5 17.25V25.0889C30.4172 25.0848 30.3338 25.083 30.25 25.083C29.8184 25.083 29.3995 25.1374 29 25.2402V25.0537C28.6427 25.2475 28.2369 25.4175 27.7998 25.5635C26.3499 26.0475 24.3907 26.333 22.25 26.333C20.1093 26.333 18.1501 26.0475 16.7002 25.5635C16.2631 25.4175 15.8573 25.2475 15.5 25.0537V28.917C15.5002 28.9389 15.5137 29.0744 15.8018 29.3027C16.085 29.5272 16.542 29.7623 17.1748 29.9736C18.4332 30.3937 20.2243 30.667 22.25 30.667C23.3382 30.667 24.3581 30.5844 25.2646 30.4463C25.3016 30.9605 25.4159 31.4527 25.5967 31.9121C24.5747 32.0741 23.4408 32.167 22.25 32.167C20.1092 32.167 18.1502 31.8805 16.7002 31.3965C15.9791 31.1558 15.342 30.8524 14.8701 30.4785C14.4059 30.1106 14.0002 29.5883 14 28.917V17.25C14 16.5766 14.41 16.054 14.875 15.6875C15.3484 15.3144 15.9877 15.0113 16.71 14.7705C18.1623 14.2865 20.121 14 22.25 14ZM29 19.2168C28.64 19.4118 28.2306 19.5826 27.79 19.7295C26.3377 20.2135 24.379 20.5 22.25 20.5C20.121 20.5 18.1623 20.2135 16.71 19.7295C16.2694 19.5826 15.86 19.4118 15.5 19.2168V23.083C15.5001 23.1039 15.5122 23.2403 15.8018 23.4697C16.0851 23.6941 16.5421 23.9294 17.1748 24.1406C18.4332 24.5607 20.2243 24.833 22.25 24.833C24.2757 24.833 26.0668 24.5607 27.3252 24.1406C27.9579 23.9294 28.4149 23.6941 28.6982 23.4697C28.9878 23.2403 28.9999 23.1039 29 23.083V19.2168ZM22.25 15.5C20.2371 15.5 18.4456 15.7727 17.1836 16.1934C16.5488 16.405 16.0893 16.6412 15.8037 16.8662C15.5099 17.0978 15.5 17.2331 15.5 17.25C15.5001 17.2679 15.5111 17.4032 15.8037 17.6338C16.0893 17.8588 16.5488 18.095 17.1836 18.3066C18.4456 18.7273 20.2371 19 22.25 19C24.2628 19 26.0544 18.7273 27.3164 18.3066C27.9512 18.095 28.4107 17.8588 28.6963 17.6338C28.9889 17.4032 28.9999 17.2679 29 17.25C29 17.2331 28.9901 17.0978 28.6963 16.8662C28.4107 16.6412 27.9512 16.405 27.3164 16.1934C26.0544 15.7727 24.2629 15.5 22.25 15.5Z M29.9166 32.4164C31.3893 32.4164 32.5831 31.2225 32.5831 29.7497C32.5831 28.2769 31.3893 27.083 29.9166 27.083C28.4439 27.083 27.25 28.2769 27.25 29.7497C27.25 31.2225 28.4439 32.4164 29.9166 32.4164Z M33.2505 33.0828L31.8005 31.6328",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "rgba(43, 189, 160, 1)",
|
||||
fillGradient: ["rgba(43, 189, 160, 1)", "rgba(43, 189, 160, 1)"],
|
||||
originalIcon: "",
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
repeat: {
|
||||
icon: "M19 8l-4 4h3c0 3.31-2.69 6-6 6-1.01 0-1.97-.25-2.8-.7l-1.46 1.46C8.97 19.54 10.43 20 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46C15.03 4.46 13.57 4 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z",
|
||||
icon: "M7.417 8.667V11.167H9.917 M16.583 15.333V12.833H14.083 M15.538 10.75C15.326 10.153 14.967 9.619 14.494 9.198C14.020 8.777 13.448 8.483 12.830 8.343C12.212 8.204 11.569 8.223 10.960 8.399C10.352 8.575 9.798 8.902 9.350 9.35L7.417 11.167M16.583 12.833L14.650 14.65C14.202 15.098 13.648 15.425 13.040 15.601C12.431 15.777 11.788 15.796 11.170 15.657C10.552 15.517 9.980 15.223 9.507 14.802C9.033 14.381 8.674 13.847 8.463 13.25",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: defaultColor,
|
||||
iconBackgroundColor: "rgba(242, 101, 59, 1)",
|
||||
fillGradient: ["rgba(242, 101, 59, 1)", "rgba(242, 101, 59, 1)"],
|
||||
originalIcon: <CachedIcon />,
|
||||
useStroke: true,
|
||||
dropdownViewBox: "5 7 14 10",
|
||||
},
|
||||
// repeat: {
|
||||
// icon: "M19 8l-4 4h3c0 3.31-2.69 6-6 6-1.01 0-1.97-.25-2.8-.7l-1.46 1.46C8.97 19.54 10.43 20 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46C15.03 4.46 13.57 4 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: defaultColor,
|
||||
// originalIcon: <CachedIcon />,
|
||||
// },
|
||||
|
||||
add: {
|
||||
icon: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: defaultColor,
|
||||
originalIcon: <AddIcon />,
|
||||
},
|
||||
// edit: {
|
||||
// icon: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: defaultColor,
|
||||
// originalIcon: <EditIcon />,
|
||||
// },
|
||||
edit: {
|
||||
icon: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z",
|
||||
icon: "M27.75 17.25C27.947 17.053 28.1808 16.8967 28.4382 16.7901C28.6956 16.6835 28.9714 16.6287 29.25 16.6287C29.5286 16.6287 29.8044 16.6835 30.0618 16.7901C30.3192 16.8967 30.553 17.053 30.75 17.25C30.947 17.447 31.1032 17.6808 31.2098 17.9382C31.3165 18.1956 31.3713 18.4714 31.3713 18.75C31.3713 19.0286 31.3165 19.3044 31.2098 19.5618C31.1032 19.8191 30.947 20.053 30.75 20.25L20.625 30.375L16.5 31.5L17.625 27.375L27.75 17.25Z",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: defaultColor,
|
||||
iconBackgroundColor: "rgba(242, 101, 59, 1)",
|
||||
fillGradient: ["rgba(242, 101, 59, 1)", "rgba(242, 101, 59, 1)"],
|
||||
originalIcon: <EditIcon />,
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
filter: {
|
||||
icon: "M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39c.51-.66.04-1.61-.79-1.61H5.04c-.83 0-1.3.95-.79 1.61z",
|
||||
// filter: {
|
||||
// icon: "M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39c.51-.66.04-1.61-.79-1.61H5.04c-.83 0-1.3.95-.79 1.61z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: "#f5515f",
|
||||
// originalIcon: "",
|
||||
// fillGradient: ["#f5515f", "#a1051d"],
|
||||
// },
|
||||
filter: {
|
||||
icon: "M31.25 17C31.25 16.8619 31.1381 16.75 31 16.75H17C16.8619 16.75 16.75 16.8619 16.75 17V18.5859C16.75 18.6522 16.7764 18.7158 16.8232 18.7627L21.2373 23.1768C21.5655 23.5049 21.75 23.95 21.75 24.4141V30.958L26.25 29.459V24.4141C26.25 23.95 26.4345 23.5049 26.7627 23.1768L31.1768 18.7627C31.2236 18.7158 31.25 18.6522 31.25 18.5859V17ZM32.75 18.5859C32.75 19.05 32.5655 19.4951 32.2373 19.8232L27.8232 24.2373C27.7764 24.2842 27.75 24.3478 27.75 24.4141V29.6396C27.75 30.1769 27.4066 30.6558 26.8955 30.8262L21.8955 32.4922C21.0874 32.7615 20.2509 32.1611 20.25 31.3076V24.4141C20.25 24.3478 20.2236 24.2842 20.1768 24.2373L15.7627 19.8232C15.4345 19.4951 15.25 19.05 15.25 18.5859V17C15.25 16.0335 16.0335 15.25 17 15.25H31C31.9665 15.25 32.75 16.0335 32.75 17V18.5859Z",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "#f5515f",
|
||||
iconBackgroundColor: "rgba(217, 56, 136, 1)",
|
||||
originalIcon: "",
|
||||
fillGradient: ["#f5515f", "#a1051d"],
|
||||
},
|
||||
fillGradient: ["rgba(217, 56, 136, 1)", "rgba(217, 56, 136, 1)"],
|
||||
fillRule: "evenodd",
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
// merge: {
|
||||
// icon: "M17 20.41 18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: "#f5515f",
|
||||
// originalIcon: "",
|
||||
// fillGradient: ["#f5515f", "#a1051d"],
|
||||
// },
|
||||
merge: {
|
||||
icon: "M17 20.41 18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z",
|
||||
icon: "M23.4692 16.2696C23.7621 15.9768 24.2369 15.9769 24.5298 16.2696L29.3032 21.0421C29.5961 21.335 29.5961 21.8107 29.3032 22.1036C29.0103 22.3963 28.5345 22.3964 28.2417 22.1036L24.7495 18.6104V23.0802C24.7497 23.0867 24.7505 23.0932 24.7505 23.0997V24.0001C24.7507 28.0592 28.0409 31.3497 32.1001 31.3497C32.5141 31.3498 32.85 31.6857 32.8501 32.0997C32.8501 32.5138 32.5142 32.8496 32.1001 32.8497C28.4809 32.8497 25.3708 30.6766 23.9995 27.5645C22.6281 30.6765 19.519 32.8497 15.8999 32.8497C15.4857 32.8497 15.1499 32.5139 15.1499 32.0997C15.15 31.6856 15.4858 31.3497 15.8999 31.3497C19.9591 31.3497 23.2493 28.0592 23.2495 24.0001V18.6104L19.7573 22.1036C19.4644 22.3963 18.9886 22.3964 18.6958 22.1036C18.4032 21.8108 18.4032 21.3349 18.6958 21.0421L23.4692 16.2696Z",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "#f5515f",
|
||||
iconBackgroundColor: "rgba(217, 56, 136, 1)",
|
||||
originalIcon: "",
|
||||
fillGradient: ["#f5515f", "#a1051d"],
|
||||
fillGradient: ["rgba(217, 56, 136, 1)", "rgba(217, 56, 136, 1)"],
|
||||
useStroke: false,
|
||||
useFill: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
// compare: {
|
||||
// icon: "M10 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h5v2h2V1h-2v2zm0 15H5l5-6v6zm9-15h-5v2h5v13l-5-6v9h5c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: defaultColor,
|
||||
// originalIcon: <CompareIcon />,
|
||||
// },
|
||||
compare: {
|
||||
icon: "M10 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h5v2h2V1h-2v2zm0 15H5l5-6v6zm9-15h-5v2h5v13l-5-6v9h5c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z",
|
||||
icon: "M19 15.75H23.7832V32.25H19C17.7574 32.25 16.75 31.2426 16.75 30V18C16.75 16.8352 17.6352 15.8771 18.7695 15.7617L19 15.75Z M26.667 16.125H29.0003C30.6572 16.125 32.0003 17.4681 32.0003 19.125V28.875C32.0003 30.5319 30.6572 31.875 29.0003 31.875H26.667",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: defaultColor,
|
||||
originalIcon: <CompareIcon />,
|
||||
iconBackgroundColor: "rgba(242, 101, 59, 1)",
|
||||
originalIcon: "",
|
||||
fillGradient: ["rgba(242, 101, 59, 1)", "rgba(242, 101, 59, 1)"],
|
||||
useStroke: true,
|
||||
useFill: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
extract: {
|
||||
icon: "M3 3h18v2H3z",
|
||||
@@ -462,18 +554,36 @@ export const GetIconInfo = (action) => {
|
||||
iconBackgroundColor: defaultColor,
|
||||
originalIcon: <TocIcon />,
|
||||
},
|
||||
// execute: {
|
||||
// icon: "M8 5v14l11-7z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: defaultColor,
|
||||
// originalIcon: <PlayArrowIcon />,
|
||||
// },
|
||||
execute: {
|
||||
icon: "M8 5v14l11-7z",
|
||||
icon: "M17.3335 28.1667L22.3335 23.1667L17.3335 18.1667 M24 29.8333H30.6667",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: defaultColor,
|
||||
iconBackgroundColor: "rgba(242, 101, 59, 1)",
|
||||
fillGradient: ["rgba(242, 101, 59, 1)", "rgba(242, 101, 59, 1)"],
|
||||
originalIcon: <PlayArrowIcon />,
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
// delete: {
|
||||
// icon: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: "#03030e",
|
||||
// originalIcon: <DeleteIcon />,
|
||||
// fillGradient: ["#03030e", "#205d66"],
|
||||
// },
|
||||
delete: {
|
||||
icon: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z",
|
||||
icon: "M15 18.5H33C31.5955 18.5 30.8933 18.5 30.3889 18.8371C30.1705 18.983 29.983 19.1705 29.8371 19.3889C29.5 19.8933 29.5 20.5955 29.5 22V27.5C29.5 29.3856 29.5 30.3284 28.9142 30.9142C28.3284 31.5 27.3856 31.5 25.5 31.5H22.5C20.6144 31.5 19.6716 31.5 19.0858 30.9142C18.5 30.3284 18.5 29.3856 18.5 27.5V22C18.5 20.5955 18.5 19.8933 18.1629 19.3889C18.017 19.1705 17.8295 18.983 17.6111 18.8371C17.1067 18.5 16.4045 18.5 15 18.5Z M21.5 15.5002C21.5 15.5002 22 14.5 24 14.5C26 14.5 26.5 15.5 26.5 15.5",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "#03030e",
|
||||
originalIcon: <DeleteIcon />,
|
||||
fillGradient: ["#03030e", "#205d66"],
|
||||
iconBackgroundColor: "rgba(98, 98, 98, 1)",
|
||||
originalIcon: "",
|
||||
fillGradient: ["rgba(98, 98, 98, 1)", "rgba(98, 98, 98, 1)"],
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
close: {
|
||||
icon: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z",
|
||||
@@ -482,19 +592,37 @@ export const GetIconInfo = (action) => {
|
||||
originalIcon: <CloseIcon />,
|
||||
fillGradient: ["#03030e", "#205d66"],
|
||||
},
|
||||
// send: {
|
||||
// icon: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: "#0373da",
|
||||
// originalIcon: <SendIcon />,
|
||||
// fillGradient: ["#0bc8bf", "#0373da"],
|
||||
// },
|
||||
send: {
|
||||
icon: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z",
|
||||
icon: "M31.3332 16.6667L22.1665 25.8334 M31.3332 16.6667L25.4998 33.3334L22.1665 25.8334L14.6665 22.5001L31.3332 16.6667Z",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "#0373da",
|
||||
originalIcon: <SendIcon />,
|
||||
fillGradient: ["#0bc8bf", "#0373da"],
|
||||
iconBackgroundColor: "rgba(128, 107, 255, 1)",
|
||||
originalIcon: "",
|
||||
fillGradient: ["rgba(128, 107, 255, 1)", "rgba(128, 107, 255 , 1)"],
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
// download: {
|
||||
// icon: "M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z",
|
||||
// iconColor: "white",
|
||||
// iconBackgroundColor: "#0373da",
|
||||
// originalIcon: <GetAppIcon />,
|
||||
// fillGradient: ["#0bc8bf", "#0373da"],
|
||||
// },
|
||||
download: {
|
||||
icon: "M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z",
|
||||
icon: "M18 22L24 28M24 28L30 22M24 28L24 16 M17 33H31",
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "#0373da",
|
||||
originalIcon: <GetAppIcon />,
|
||||
fillGradient: ["#0bc8bf", "#0373da"],
|
||||
iconBackgroundColor: "rgba(128, 107, 255, 1)",
|
||||
originalIcon: "",
|
||||
fillGradient: ["rgba(128, 107, 255, 1)", "rgba(128, 107, 255, 1)"],
|
||||
useStroke: true,
|
||||
svgViewBox: "0 0 48 48",
|
||||
},
|
||||
search: {
|
||||
icon: "M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z",
|
||||
@@ -537,7 +665,7 @@ export const GetIconInfo = (action) => {
|
||||
iconColor: "white",
|
||||
iconBackgroundColor: "green",
|
||||
originalIcon: <LockIcon />,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -568,28 +696,59 @@ export const GetIconInfo = (action) => {
|
||||
}
|
||||
|
||||
if (
|
||||
(selectedItem.originalIcon === undefined ||
|
||||
selectedItem.originalIcon === "") &&
|
||||
selectedItem.icon !== "" &&
|
||||
selectedItem.icon !== undefined
|
||||
) {
|
||||
const svg_pin = (
|
||||
<svg
|
||||
width={svgSize}
|
||||
height={svgSize}
|
||||
viewBox={`0 0 ${svgSize} ${svgSize}`}
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d={selectedItem.icon} fill={selectedItem.iconColor}></path>
|
||||
</svg>
|
||||
);
|
||||
const iconViewBox = selectedItem.svgViewBox || `0 0 ${svgSize} ${svgSize}`;
|
||||
// Use a tighter viewBox for dropdown icons so 48x48 centered content appears larger
|
||||
const dropdownViewBox = selectedItem.dropdownViewBox || (iconViewBox === "0 0 48 48" ? "10 10 28 28" : iconViewBox);
|
||||
const strokeW = iconViewBox === "0 0 48 48" ? "1.5" : "1";
|
||||
// Build dropdown React SVG using the same stroke/fill logic as buildIconSvgPath
|
||||
var svg_pin;
|
||||
if (selectedItem.useStroke && selectedItem.useFill) {
|
||||
svg_pin = (
|
||||
<svg width={22} height={22} viewBox={dropdownViewBox} version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d={selectedItem.icon} fill="currentColor" stroke="currentColor" strokeWidth={strokeW} strokeLinecap="round" strokeLinejoin="round"></path>
|
||||
</svg>
|
||||
);
|
||||
} else if (selectedItem.useStroke) {
|
||||
svg_pin = (
|
||||
<svg width={22} height={22} viewBox={dropdownViewBox} version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d={selectedItem.icon} fill="none" stroke="currentColor" strokeWidth={strokeW} strokeLinecap="round" strokeLinejoin="round"></path>
|
||||
</svg>
|
||||
);
|
||||
} else {
|
||||
svg_pin = (
|
||||
<svg width={22} height={22} viewBox={dropdownViewBox} version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d={selectedItem.icon} fill="currentColor" fillRule={selectedItem.fillRule || "nonzero"}></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
selectedItem.originalIcon = svg_pin;
|
||||
}
|
||||
|
||||
return selectedItem;
|
||||
};
|
||||
|
||||
// Builds the SVG <path .../> string for an icon with the proper fill/stroke attributes.
|
||||
// color: the fill/stroke color (e.g. "white" for cytoscape nodes, "currentColor" for React)
|
||||
// Returns an SVG path element string ready to embed inside an <svg> tag.
|
||||
export const buildIconSvgPath = (iconInfo, color) => {
|
||||
const iconViewBox = iconInfo.svgViewBox || "0 0 24 24";
|
||||
// For stroke icons: use proportional stroke width (0.75 for 24x24, 1.5 for 48x48)
|
||||
// This ensures consistent visual appearance across different viewBox sizes
|
||||
const strokeW = iconInfo.useStroke
|
||||
? (iconViewBox === "0 0 48 48" ? "1.5" : "0.75")
|
||||
: (iconInfo.useFill ? "0" : "1.5");
|
||||
if (iconInfo.useStroke && iconInfo.useFill) {
|
||||
return `<path d="${iconInfo.icon}" fill="${color}" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-linejoin="round"/>`;
|
||||
} else if (iconInfo.useStroke) {
|
||||
return `<path d="${iconInfo.icon}" fill="none" stroke="${color}" stroke-width="${strokeW}" stroke-linecap="round" stroke-linejoin="round"/>`;
|
||||
} else {
|
||||
return `<path d="${iconInfo.icon}" fill="${color}"${iconInfo.fillRule ? ` fill-rule="${iconInfo.fillRule}"` : ""}/>`;
|
||||
}
|
||||
};
|
||||
|
||||
export const collapseField = (field, inputdata) => {
|
||||
if (field === undefined || field === null) {
|
||||
return true
|
||||
@@ -625,7 +784,7 @@ export const validateJson = (showResult) => {
|
||||
if (showResult === undefined || showResult === null) {
|
||||
return {
|
||||
valid: false,
|
||||
result: "",
|
||||
result: showResult,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,7 +797,10 @@ export const validateJson = (showResult) => {
|
||||
}
|
||||
|
||||
if (typeof showResult === "object" || typeof showResult === "array") {
|
||||
return {
|
||||
//console.log("Valid!: ", showResult)
|
||||
// FIXME: Not returning as we want to recurse.
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
result: showResult,
|
||||
}
|
||||
@@ -720,64 +882,85 @@ export const validateJson = (showResult) => {
|
||||
}
|
||||
|
||||
// This is where we start recursing
|
||||
// Problem: Loops aren't recursed properly.
|
||||
if (jsonvalid) {
|
||||
// Check fields if they can be parsed too
|
||||
try {
|
||||
for (const [key, value] of Object.entries(result)) {
|
||||
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
|
||||
//console.log("CHECKING STRING: ", value)
|
||||
if (value === null || value === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
const inside_result = validateJson(value)
|
||||
if (inside_result.valid) {
|
||||
//console.log("INSIDE RESULT: ", inside_result.result)
|
||||
try {
|
||||
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
|
||||
//console.log("CHECKING STRING: ", value)
|
||||
|
||||
if (typeof inside_result.result === "string") {
|
||||
const newres = JSON.parse(inside_result.result)
|
||||
const inside_result = validateJson(value)
|
||||
if (inside_result.valid) {
|
||||
if (typeof inside_result.result === "string") {
|
||||
const newres = JSON.parse(inside_result.result)
|
||||
|
||||
result[key] = newres
|
||||
} else {
|
||||
result[key] = inside_result.result
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result[key] = newres
|
||||
} else {
|
||||
result[key] = inside_result.result
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Usually only reaches here if raw array > dict > value
|
||||
if (typeof showResult !== "array") {
|
||||
for (const [subkey, subvalue] of Object.entries(value)) {
|
||||
if (typeof subvalue === "string" && (subvalue.startsWith("{") || subvalue.startsWith("["))) {
|
||||
const inside_result = validateJson(subvalue)
|
||||
if (inside_result.valid) {
|
||||
if (typeof inside_result.result === "string") {
|
||||
const newres = JSON.parse(inside_result.result)
|
||||
result[key][subkey] = newres
|
||||
} else {
|
||||
result[key][subkey] = inside_result.result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usually only reaches here if raw array > dict > value
|
||||
if (typeof showResult !== "array") {
|
||||
for (const [subkey, subvalue] of Object.entries(value)) {
|
||||
if (typeof subvalue === "string" && (subvalue.startsWith("{") || subvalue.startsWith("["))) {
|
||||
const inside_result = validateJson(subvalue)
|
||||
if (inside_result.valid) {
|
||||
if (typeof inside_result.result === "string") {
|
||||
const newres = JSON.parse(inside_result.result)
|
||||
result[key][subkey] = newres
|
||||
} else {
|
||||
result[key][subkey] = inside_result.result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("Skip array: ", value)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Failed to parse '${key}': '${value}': `, e, JSON.stringify(result))
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
//console.log("Failed parsing inside json subvalues: ", e)
|
||||
console.log("Failed parsing inside json subvalues: ", e, result)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
valid: jsonvalid,
|
||||
//console.log("Result: ", result)
|
||||
|
||||
try {
|
||||
const stringified = JSON.stringify(result)
|
||||
if ((stringified.startsWith("{") && stringified.endsWith("}")) || (stringified.startsWith("[") && stringified.endsWith("]"))) {
|
||||
result = JSON.parse(stringified)
|
||||
jsonvalid = true
|
||||
}
|
||||
} catch(e) {
|
||||
jsonvalid = false
|
||||
}
|
||||
|
||||
const toReturn = {
|
||||
result: result,
|
||||
};
|
||||
valid: jsonvalid,
|
||||
}
|
||||
|
||||
return toReturn
|
||||
};
|
||||
|
||||
//Custom hook for handling styling of the dropzone
|
||||
const useDropzoneStyles = () => {
|
||||
const { leftSideBarOpenByClick } = useContext(Context);
|
||||
const { themeMode, brandColor } = useContext(Context);
|
||||
const { leftSideBarOpenByClick, themeMode, brandColor } = useContext(Context);
|
||||
const theme = getTheme(themeMode, brandColor);
|
||||
|
||||
return {
|
||||
return React.useMemo(() => ({
|
||||
paddingTop: 70,
|
||||
// minHeight: 1000,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
@@ -785,7 +968,7 @@ const useDropzoneStyles = () => {
|
||||
// maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
|
||||
paddingLeft: leftSideBarOpenByClick ? 200 : 0,
|
||||
transition: "padding-left 0.3s ease",
|
||||
};
|
||||
}), [leftSideBarOpenByClick, theme]);
|
||||
};
|
||||
|
||||
//Wrapper for the dropzone component
|
||||
@@ -798,8 +981,6 @@ const DropzoneWrapper = memo(({ onDrop, WorkflowView }) => {
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
const Workflows2 = (props) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
|
||||
const location = useLocation();
|
||||
@@ -891,7 +1072,7 @@ const Workflows2 = (props) => {
|
||||
const [field1, setField1] = React.useState("");
|
||||
const [field2, setField2] = React.useState("");
|
||||
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/workflows")
|
||||
const [downloadBranch, setDownloadBranch] = React.useState("master");
|
||||
const [downloadBranch, setDownloadBranch] = React.useState("main");
|
||||
const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] =
|
||||
React.useState(false);
|
||||
const [exportModalOpen, setExportModalOpen] = React.useState(false);
|
||||
@@ -2903,7 +3084,7 @@ const Workflows2 = (props) => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
|
||||
var boxColor = "#FECC00";
|
||||
var boxColor = "#FFC633";
|
||||
if (data.is_valid) {
|
||||
boxColor = "#86c142";
|
||||
}
|
||||
@@ -3891,7 +4072,7 @@ const Workflows2 = (props) => {
|
||||
renderCell: (params) => {
|
||||
const data = params.row.record;
|
||||
|
||||
var boxColor = "#FECC00";
|
||||
var boxColor = "#FFC633";
|
||||
if (data.is_valid) {
|
||||
boxColor = "#86c142";
|
||||
}
|
||||
@@ -5097,7 +5278,7 @@ const Workflows2 = (props) => {
|
||||
margin: "auto",
|
||||
}}>
|
||||
<Typography variant="h4" color="textPrimary" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme.typography?.fontFamily }}>
|
||||
{currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Workflows
|
||||
{currTab === 0 ? "Org" : currTab === 1 ? "Your" : currTab === 2 ? "Discover" : "Security Bundle" } Workflows
|
||||
</Typography>
|
||||
|
||||
<div style={{ borderBottom: themeMode === "dark" ? "1px solid #808080" : theme.palette.defaultBorder, marginBottom: 30 }}>
|
||||
@@ -5491,13 +5672,22 @@ const Workflows2 = (props) => {
|
||||
|
||||
<Tooltip title={`Download ALL workflows (${workflows.length})`} placement="top">
|
||||
<IconButton
|
||||
style={(isCloud || currTab === 2) ? iconButtonDisabledStyle : { ...iconButtonStyle, cursor: "pointer" }}
|
||||
disabled={isCloud || currTab === 2}
|
||||
style={(currTab === 2) ? iconButtonDisabledStyle : { ...iconButtonStyle, cursor: "pointer" }}
|
||||
disabled={currTab === 2}
|
||||
onClick={() => exportAllWorkflows(workflows)}
|
||||
>
|
||||
<GetAppIcon style={{ color: theme.palette.text.primary, opacity: currTab === 2 ? 0.5 : 1}} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Import workflows from URL" placement="top">
|
||||
<IconButton
|
||||
style={currTab === 2 ? iconButtonDisabledStyle : iconButtonStyle}
|
||||
onClick={() => setLoadWorkflowsModalOpen(true)}
|
||||
disabled={currTab === 2}
|
||||
>
|
||||
<CloudDownloadIcon style={{ color: theme.palette.text.primary, opacity: currTab === 2 ? 0.5 : 1}} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -5520,48 +5710,18 @@ const Workflows2 = (props) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
{!isCloud && currentOrg?.old_org ? (
|
||||
{!isCloud && (currentOrg?.old_org || isProdStatusOn) ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 20,
|
||||
padding: "4px 10px",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
borderRadius: 20,
|
||||
marginBottom: "14px",
|
||||
background: isProdStatusOn
|
||||
? "rgba(43, 192, 126, 0.1)"
|
||||
: "rgba(255, 82, 82, 0.1)",
|
||||
cursor: "pointer",
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 20,
|
||||
right: 20,
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/admin?admin_tab=billingstats")
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
marginLeft: 10,
|
||||
background: isProdStatusOn ? "#2BC07E" : "#FD4C62",
|
||||
borderRadius: 999,
|
||||
display: "inline",
|
||||
}}
|
||||
<Licensed
|
||||
licensed={isProdStatusOn}
|
||||
showBadge={true}
|
||||
/>
|
||||
<Typography
|
||||
style={{
|
||||
fontFamily: "12px",
|
||||
opacity: 0.9,
|
||||
color: isProdStatusOn ? "#2BC07E" : "#FD4C62",
|
||||
}}
|
||||
>
|
||||
{isProdStatusOn ? "Production" : "NOT Production"}
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -5812,15 +5972,15 @@ const Workflows2 = (props) => {
|
||||
|
||||
const parsedData = {
|
||||
url: url,
|
||||
field_3: downloadBranch || "master",
|
||||
branch: downloadBranch || "master",
|
||||
};
|
||||
|
||||
if (field1.length > 0) {
|
||||
parsedData["field_1"] = field1;
|
||||
parsedData["username"] = field1;
|
||||
}
|
||||
|
||||
if (field2.length > 0) {
|
||||
parsedData["field_2"] = field2;
|
||||
parsedData["password"] = field2;
|
||||
}
|
||||
|
||||
toast("Getting specific workflows from your URL.");
|
||||
@@ -5867,17 +6027,28 @@ const Workflows2 = (props) => {
|
||||
open={loadWorkflowsModalOpen}
|
||||
onClose={() => { }}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
sx: {
|
||||
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
|
||||
border: theme?.palette?.DialogStyle?.border,
|
||||
minWidth: '440px',
|
||||
fontFamily: theme?.typography?.fontFamily,
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
zIndex: 1000,
|
||||
'& .MuiDialogContent-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
'& .MuiDialogTitle-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
'& .MuiDialogActions-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Load workflows from github repo
|
||||
<div style={{ color: theme.palette.text.primary }}>
|
||||
Load workflows from a remote repository
|
||||
<div style={{ float: "right" }}>
|
||||
<Tooltip color="primary" title={"Import manually"} placement="top">
|
||||
<Button
|
||||
@@ -5892,17 +6063,17 @@ const Workflows2 = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
Repository (supported: github, gitlab, bitbucket)
|
||||
<DialogContent style={{ color: theme.palette.text.primary }}>
|
||||
Repository (supported: github, gitlab, bitbucket, azure devops):
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
style={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.textFieldStyle.color, borderRadius: theme.palette.textFieldStyle.borderRadius, height: theme.palette.textFieldStyle.height, border: theme.palette.textFieldStyle.border, }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadUrl}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
color: theme.palette.textFieldStyle.color,
|
||||
height: theme.palette.textFieldStyle.height,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
@@ -5915,34 +6086,36 @@ const Workflows2 = (props) => {
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
style={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.textFieldStyle.color, borderRadius: theme.palette.textFieldStyle.borderRadius, height: theme.palette.textFieldStyle.height, border: theme.palette.textFieldStyle.border, }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadBranch}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
color: theme.palette.textFieldStyle.color,
|
||||
height: theme.palette.textFieldStyle.height,
|
||||
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadBranch(e.target.value)}
|
||||
placeholder="master"
|
||||
placeholder="main"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Authentication (optional - private repos etc):
|
||||
Authentication for private repositories (optional):
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
style={{ flex: 1, backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.textFieldStyle.color, borderRadius: theme.palette.textFieldStyle.borderRadius, height: theme.palette.textFieldStyle.height, border: theme.palette.textFieldStyle.border, }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
color: theme.palette.textFieldStyle.color,
|
||||
height: theme.palette.textFieldStyle.height,
|
||||
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
@@ -5952,13 +6125,13 @@ const Workflows2 = (props) => {
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
style={{ flex: 1, backgroundColor: theme.palette.textFieldStyle.backgroundColor, color: theme.palette.textFieldStyle.color, borderRadius: theme.palette.textFieldStyle.borderRadius, height: theme.palette.textFieldStyle.height, border: theme.palette.textFieldStyle.border, }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
color: theme.palette.textFieldStyle.color ,
|
||||
height: theme.palette.textFieldStyle.height,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
@@ -5978,12 +6151,12 @@ const Workflows2 = (props) => {
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={downloadUrl.length === 0 || !downloadUrl.includes("http")}
|
||||
onClick={() => {
|
||||
handleGithubValidation();
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:5001',
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
build: {
|
||||
outDir: 'build',
|
||||
sourcemap: false,
|
||||
},
|
||||
esbuild: {
|
||||
loader: 'jsx',
|
||||
include: /src\/.*\.jsx?$/,
|
||||
exclude: [],
|
||||
},
|
||||
optimizeDeps: {
|
||||
esbuildOptions: {
|
||||
loader: {
|
||||
'.js': 'jsx',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -2,7 +2,7 @@ apiVersion: v2
|
||||
name: shuffle
|
||||
description: A Helm chart for deploying Shuffle on Kubernetes
|
||||
type: application
|
||||
version: 2.1.0 # Set during publishing in GitHub actions
|
||||
version: 0.0.0 # Set during publishing in GitHub actions
|
||||
appVersion: latest # Overwritten during publishing in GitHub actions
|
||||
dependencies:
|
||||
- name: common
|
||||
|
||||
@@ -105,6 +105,105 @@ SHUFFLE_DEFAULT_APIKEY: "72E41083-A6F6-4A1B-8538-B06B577F47F0" # Shuffle uses uu
|
||||
SHUFFLE_ENCRYPTION_MODIFIER: "MyShuffleEncryptionModifier"
|
||||
```
|
||||
|
||||
## Shuffle Worker
|
||||
|
||||
By default, Orborus creates a Kubernetes Deployment and Service for Shuffle Worker.
|
||||
The deployment can be customized to some degree using some of the `worker.*` helm variables. They are converted to Orborus env variables.
|
||||
|
||||
If you want full control, you can also deploy Shuffle Worker using helm by enabling `worker.enableHelmDeployment`.
|
||||
This approach respects all of the `worker.*` helm variables.
|
||||
|
||||
You can then set `orborus.manageWorkerDeployments=false` to reduce the permissions assigned to the Shuffle Orborus Kubernetes service account.
|
||||
|
||||
## Shuffle Apps
|
||||
|
||||
By default, Shuffle Worker is responsible for creating Kubernetes Deployments and Services for each app.
|
||||
Each app and version has their own Deployment and Service. Shuffle automatically deploys a set of apps.
|
||||
Other apps are deployed on demand, when they are first used.
|
||||
|
||||
You can use some of the `app.*` helm variables to control some aspects of the deployment, e.g. resources and security context.
|
||||
Helm variables are converted to env variables set on Orborus. Orborus in turn passes the env variables to Worker when creating the Deployment.
|
||||
When `worker.enableHelmDeployment` is set, env variables for app configuration are set on the worker directly.
|
||||
Configuration using env variables applies to ALL deployed apps. There is no way to assign different options (e.g. resources) to different apps, or scale apps individually.
|
||||
|
||||
If you want full control, you can deploy apps using helm. This has the following advantages:
|
||||
|
||||
- full control over the deployment using helm values
|
||||
- granular control per app and version (e.g. have more replicas and resources for frequently used apps)
|
||||
- avoid problems with on-demand started apps (see https://github.com/Shuffle/Shuffle/issues/1739)
|
||||
|
||||
To deploy apps using helm, set `apps.enabled=true`. By default, this deploys the `shuffle-tools`, `shuffle-subflow` and `http` apps.
|
||||
You can also deploy your own apps. See the following values file for an example.
|
||||
|
||||
```yaml
|
||||
app:
|
||||
replicaCount: 1 # default to 1 replica per app
|
||||
resources: {} # default resources for apps
|
||||
# ... configure default options for all apps here
|
||||
|
||||
apps:
|
||||
enabled: true # Deploy apps using helm.
|
||||
|
||||
# Configure default apps
|
||||
shuffleTools:
|
||||
enabled: true # default
|
||||
shuffleSubflow:
|
||||
enabled: true # default
|
||||
http:
|
||||
enabled: true # default
|
||||
# optionally override defaults from app values:
|
||||
replicaCount: 1
|
||||
resources: {}
|
||||
|
||||
# Deploy additional apps (e.g. opensearch)
|
||||
opensearch:
|
||||
enabled: true # required to actually deploy the app
|
||||
name: opensearch # required. The name and version must match the values of the `api.yaml` file of the app.
|
||||
version: 1.1.0 # required.
|
||||
# optionally change app configuration:
|
||||
replicaCount: 3
|
||||
resources: {}
|
||||
```
|
||||
|
||||
The key of an app in the `apps` map does not matter, as long as it is unique. We are not using an array here, to allow overriding values in stage-specific value files or using the command line, e.g.
|
||||
`helm upgrade ... --set apps.shuffleTools.replicas=3`.
|
||||
|
||||
You can override any value set in `app.*` (e.g. `app.image`, `app.replicaCount`, `app.resources`, `app.podSecurityContext`) for each app
|
||||
(e.g. for the `shuffle-tools` app using `apps.shuffleTools.image`, `apps.shuffleTools.replicaCount`, ...).
|
||||
|
||||
It is possible to use a hybrid approach - deploy some apps using helm, while still allowing Worker to create additional apps on-demand.
|
||||
|
||||
If you do not want Worker to manage app deployments, set `worker.manageAppDeployments=true`. This effectively removes the required permissions from the Shuffle Worker Kubernetes Service Account.
|
||||
You are required to deploy all apps that are in use by your Shuffle instance manually using Helm.
|
||||
|
||||
### Shuffle App Service Accounts
|
||||
|
||||
By default a shared `shuffle-app` service account is used for all apps.
|
||||
If you are deploying apps using helm, you can choose to have a dedicated service account per app.
|
||||
To enable it, set `apps.MY_APP.serviceAccount.create=true` and provide a name using `apps.MY_APP.serviceAccount.name`.
|
||||
You can also set `apps.MY_APP.serviceAccount.create=false` while still providing a name to use an existing service account.
|
||||
|
||||
```yaml
|
||||
apps:
|
||||
myAppWithCustomServiceAccount:
|
||||
enabled: true
|
||||
name: my-custom-service-account
|
||||
version: 1.0.0
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: shuffle-app-myapp
|
||||
|
||||
anotherAppWithExistingServiceAccount:
|
||||
enabled: true
|
||||
name: another-app
|
||||
version: 1.0.0
|
||||
serviceAccount:
|
||||
create: false
|
||||
name: existing-service-account-name
|
||||
```
|
||||
|
||||
All service accounts use the `shuffle-app` role by default.
|
||||
|
||||
## OpenSearch
|
||||
|
||||
Shuffle uses OpenSearch as its database. This helm chart installs a single-node OpenSearch cluster using [the Bitnami Helm Chart](https://github.com/bitnami/charts/blob/main/bitnami/opensearch/values.yaml).
|
||||
@@ -116,7 +215,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
|
||||
## Parameters
|
||||
|
||||
### Global parameters
|
||||
##### Global parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
@@ -126,7 +225,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `global.compatibility.openshift.adaptSecurityContext` | Adapt the securityContext sections of the deployment to make them compatible with Openshift restricted-v2 SCC: remove runAsUser, runAsGroup and fsGroup and let the platform use their allowed default IDs. Possible values: auto (apply if the detected running cluster is Openshift), force (perform the adaptation always), disabled (do not perform adaptation) | `auto` |
|
||||
| `global.compatibility.omitEmptySeLinuxOptions` | If set to true, removes the seLinuxOptions from the securityContexts when it is set to an empty object | `false` |
|
||||
|
||||
### Common parameters
|
||||
##### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------- | --------------- |
|
||||
@@ -142,16 +241,17 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `diagnosticMode.command` | Command to override all containers in the chart release | `["sleep"]` |
|
||||
| `diagnosticMode.args` | Args to override all containers in the chart release | `["infinity"]` |
|
||||
|
||||
### Shared Shuffle Parameters
|
||||
##### Shared Shuffle Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| --------------------- | ------------------------------------------------------------- | --------------- |
|
||||
| `shuffle.baseUrl` | The external base URL under which Shuffle is reachable. | `""` |
|
||||
| `shuffle.org` | Default shuffle organization | `Shuffle` |
|
||||
| `shuffle.appRegistry` | The registry from / to which shuffle apps are pulled / pushed | `""` |
|
||||
| `shuffle.timezone` | The timezone used by Shuffle | `Europe/Berlin` |
|
||||
| Name | Description | Value |
|
||||
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------- |
|
||||
| `shuffle.baseUrl` | The external base URL under which Shuffle is reachable. | `""` |
|
||||
| `shuffle.org` | Default shuffle organization | `Shuffle` |
|
||||
| `shuffle.appRegistry` | The registry from / to which shuffle apps are pulled / pushed | `docker.io` |
|
||||
| `shuffle.appBaseImageName` | The base image used for shuffle apps. The final image for an app is <appRegistr>/<appBaseImageName>/<appName>:<appVersion> | `frikky` |
|
||||
| `shuffle.timezone` | The timezone used by Shuffle | `Europe/Berlin` |
|
||||
|
||||
### backend Parameters
|
||||
##### backend Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
||||
@@ -266,7 +366,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `backend.apps.downloadBranch` | The branch from which apps should be downloaded on startup. | `master` |
|
||||
| `backend.apps.forceUpdate` | Force an update of apps on startup. | `false` |
|
||||
|
||||
### frontend Parameters
|
||||
##### frontend Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
|
||||
@@ -372,7 +472,7 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `frontend.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` |
|
||||
| `frontend.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` |
|
||||
|
||||
### orborus Parameters
|
||||
##### orborus Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
|
||||
@@ -475,15 +575,44 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `orborus.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` |
|
||||
| `orborus.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` |
|
||||
| `orborus.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` |
|
||||
| `orborus.executionConcurrency` | The maximum amount of concurrent workflow executions per worker | `25` |
|
||||
| `orborus.manageWorkerDeployments` | Whether workers are deployed and managed by orborus. When disabled, every worker is expected to be already deployed (see worker.enableHelmDeployment). | `true` |
|
||||
|
||||
### worker Parameters
|
||||
##### worker Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
||||
| `worker.enableHelmDeployment` | Deploy worker via helm. By default, workers are deployed by Orborus. | `false` |
|
||||
| `worker.image.registry` | worker image registry | `ghcr.io` |
|
||||
| `worker.image.repository` | worker image repository | `shuffle/shuffle-worker` |
|
||||
| `worker.image.tag` | worker image tag (immutable tags are recommended, defaults to appVersion) | `""` |
|
||||
| `worker.image.digest` | worker image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag image tag (immutable tags are recommended) | `""` |
|
||||
| `worker.image.pullPolicy` | worker image pull policy. Only effective with worker.enableHelmDeployment. | `IfNotPresent` |
|
||||
| `worker.image.pullSecrets` | worker image pull secrets. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.replicaCount` | Number of worker replicas to deploy. Only effective with worker.enableHelmDeployment. | `1` |
|
||||
| `worker.containerPorts.http` | backend HTTP container port | `33333` |
|
||||
| `worker.extraContainerPorts` | Optionally specify extra list of additional ports for worker containers. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.livenessProbe.enabled` | Enable livenessProbe on worker containers. Only effective with worker.enableHelmDeployment. | `false` |
|
||||
| `worker.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `0` |
|
||||
| `worker.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `15` |
|
||||
| `worker.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` |
|
||||
| `worker.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `4` |
|
||||
| `worker.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` |
|
||||
| `worker.readinessProbe.enabled` | Enable readinessProbe on worker containers. Only effective with worker.enableHelmDeployment. | `false` |
|
||||
| `worker.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `0` |
|
||||
| `worker.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `5` |
|
||||
| `worker.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` |
|
||||
| `worker.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` |
|
||||
| `worker.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` |
|
||||
| `worker.startupProbe.enabled` | Enable startupProbe on worker containers. Only effective with worker.enableHelmDeployment. | `false` |
|
||||
| `worker.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `0` |
|
||||
| `worker.startupProbe.periodSeconds` | Period seconds for startupProbe | `1` |
|
||||
| `worker.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` |
|
||||
| `worker.startupProbe.failureThreshold` | Failure threshold for startupProbe | `60` |
|
||||
| `worker.startupProbe.successThreshold` | Success threshold for startupProbe | `1` |
|
||||
| `worker.customLivenessProbe` | Custom livenessProbe that overrides the default one. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.customReadinessProbe` | Custom readinessProbe that overrides the default one. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.customStartupProbe` | Custom startupProbe that overrides the default one. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.resourcesPreset` | Set worker container resources according to one common preset (allowed values: none, nano, small, medium, large, xlarge, 2xlarge). This is ignored if worker.resources is set (worker.resources is recommended for production). | `nano` |
|
||||
| `worker.resources` | Set worker container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` |
|
||||
| `worker.podSecurityContext.enabled` | Enable worker pods' Security Context | `true` |
|
||||
@@ -501,6 +630,49 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `worker.containerSecurityContext.allowPrivilegeEscalation` | Set allowPrivilegeEscalation in worker container' Security Context | `false` |
|
||||
| `worker.containerSecurityContext.capabilities.drop` | List of capabilities to be dropped in worker container | `["ALL"]` |
|
||||
| `worker.containerSecurityContext.seccompProfile.type` | Set seccomp profile in worker container | `RuntimeDefault` |
|
||||
| `worker.command` | Override default worker container command (useful when using custom images). Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.args` | Override default worker container args (useful when using custom images). Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.automountServiceAccountToken` | Mount Service Account token in worker pods. Only effective with worker.enableHelmDeployment. | `true` |
|
||||
| `worker.hostAliases` | worker pods host aliases. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.deploymentAnnotations` | Annotations for worker deployment. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.podLabels` | Extra labels for worker pods. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.podAnnotations` | Annotations for worker pods. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.podAffinityPreset` | Pod affinity preset. Ignored if `worker.affinity` is set. Allowed values: `soft` or `hard`. Only effective with worker.enableHelmDeployment. | `""` |
|
||||
| `worker.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `worker.affinity` is set. Allowed values: `soft` or `hard`. Only effective with worker.enableHelmDeployment. | `soft` |
|
||||
| `worker.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `worker.affinity` is set. Allowed values: `soft` or `hard`. Only effective with worker.enableHelmDeployment. | `""` |
|
||||
| `worker.nodeAffinityPreset.key` | Node label key to match. Ignored if `worker.affinity` is set | `""` |
|
||||
| `worker.nodeAffinityPreset.values` | Node label values to match. Ignored if `worker.affinity` is set | `[]` |
|
||||
| `worker.affinity` | Affinity for worker pods assignment. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.nodeSelector` | Node labels for worker pods assignment. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.tolerations` | Tolerations for worker pods assignment. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.updateStrategy.type` | worker deployment strategy type. Only effective with worker.enableHelmDeployment. | `RollingUpdate` |
|
||||
| `worker.priorityClassName` | worker pods' priorityClassName. Only effective with worker.enableHelmDeployment. | `""` |
|
||||
| `worker.topologySpreadConstraints` | Topology Spread Constraints for worker pod assignment spread across your cluster among failure-domains. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.schedulerName` | Name of the k8s scheduler (other than default) for worker pods. Only effective with worker.enableHelmDeployment. | `""` |
|
||||
| `worker.terminationGracePeriodSeconds` | Seconds worker pods need to terminate gracefully. Only effective with worker.enableHelmDeployment. | `""` |
|
||||
| `worker.lifecycleHooks` | for worker containers to automate configuration before or after startup. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.extraEnvVars` | Array with extra environment variables to add to worker containers. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for worker containers. Only effective with worker.enableHelmDeployment. | `""` |
|
||||
| `worker.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for worker containers. Only effective with worker.enableHelmDeployment. | `""` |
|
||||
| `worker.extraVolumes` | Optionally specify extra list of additional volumes for the worker pods. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the worker containers. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.sidecars` | Add additional sidecar containers to the worker pods. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.initContainers` | Add additional init containers to the worker pods. Only effective with worker.enableHelmDeployment. | `[]` |
|
||||
| `worker.pdb.create` | Enable/disable a Pod Disruption Budget creation. Only effective with worker.enableHelmDeployment. | `true` |
|
||||
| `worker.pdb.minAvailable` | Minimum number/percentage of pods that should remain scheduled | `""` |
|
||||
| `worker.pdb.maxUnavailable` | Maximum number/percentage of pods that may be made unavailable. Defaults to `1` if both `worker.pdb.minAvailable` and `worker.pdb.maxUnavailable` are empty. | `""` |
|
||||
| `worker.autoscaling.vpa.enabled` | Enable VPA for worker pods. Only effective with worker.enableHelmDeployment. | `false` |
|
||||
| `worker.autoscaling.vpa.annotations` | Annotations for VPA resource | `{}` |
|
||||
| `worker.autoscaling.vpa.controlledResources` | VPA List of resources that the vertical pod autoscaler can control. Defaults to cpu and memory | `[]` |
|
||||
| `worker.autoscaling.vpa.maxAllowed` | VPA Max allowed resources for the pod | `{}` |
|
||||
| `worker.autoscaling.vpa.minAllowed` | VPA Min allowed resources for the pod | `{}` |
|
||||
| `worker.autoscaling.vpa.updatePolicy.updateMode` | Autoscaling update policy | `Auto` |
|
||||
| `worker.autoscaling.hpa.enabled` | Enable HPA for worker pods. Only effective with worker.enableHelmDeployment. | `false` |
|
||||
| `worker.autoscaling.hpa.minReplicas` | Minimum number of replicas | `""` |
|
||||
| `worker.autoscaling.hpa.maxReplicas` | Maximum number of replicas | `""` |
|
||||
| `worker.autoscaling.hpa.targetCPU` | Target CPU utilization percentage | `""` |
|
||||
| `worker.autoscaling.hpa.targetMemory` | Target Memory utilization percentage | `""` |
|
||||
| `worker.service.labels` | Extra labels for worker service. Only effective with worker.enableHelmDeployment. | `{}` |
|
||||
| `worker.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` |
|
||||
| `worker.serviceAccount.name` | The name of the ServiceAccount to use. | `""` |
|
||||
| `worker.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` |
|
||||
@@ -512,11 +684,40 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `worker.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` |
|
||||
| `worker.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` |
|
||||
| `worker.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` |
|
||||
| `worker.manageAppDeployments` | Whether apps are deployed and managed by worker. When disabled, every used app is expected to to be already deployed (see apps.enabled). | `true` |
|
||||
|
||||
### app Parameters
|
||||
##### app Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
|
||||
| `app.image.registry` | app image registry (defaults to shuffle.appRegistry) | `""` |
|
||||
| `app.image.repository` | app image repository (defaults to shuffle.appBaseImageName) | `""` |
|
||||
| `app.image.tag` | app image tag (defaults to the apps version) | `""` |
|
||||
| `app.image.pullPolicy` | default image pull policy for app deployments. Only effective for helm-deployed apps (see apps.enabled). | `IfNotPresent` |
|
||||
| `app.image.pullSecrets` | default image pull secrets for app deployments. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.replicaCount` | Default number of replicas to deploy for each app. Only effective for helm-deployed apps (see apps.enabled). | `1` |
|
||||
| `app.extraContainerPorts` | Optionally specify extra list of additional ports for app containers. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.livenessProbe.enabled` | Enable livenessProbe on app containers. Only effective for helm-deployed apps (see apps.enabled). | `false` |
|
||||
| `app.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `0` |
|
||||
| `app.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `15` |
|
||||
| `app.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` |
|
||||
| `app.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `4` |
|
||||
| `app.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` |
|
||||
| `app.readinessProbe.enabled` | Enable readinessProbe on app containers. Only effective for helm-deployed apps (see apps.enabled). | `false` |
|
||||
| `app.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `0` |
|
||||
| `app.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `5` |
|
||||
| `app.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` |
|
||||
| `app.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` |
|
||||
| `app.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` |
|
||||
| `app.startupProbe.enabled` | Enable startupProbe on app containers. Only effective for helm-deployed apps (see apps.enabled). | `false` |
|
||||
| `app.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `0` |
|
||||
| `app.startupProbe.periodSeconds` | Period seconds for startupProbe | `1` |
|
||||
| `app.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` |
|
||||
| `app.startupProbe.failureThreshold` | Failure threshold for startupProbe | `60` |
|
||||
| `app.startupProbe.successThreshold` | Success threshold for startupProbe | `1` |
|
||||
| `app.customLivenessProbe` | Custom livenessProbe that overrides the default one. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.customReadinessProbe` | Custom readinessProbe that overrides the default one. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.customStartupProbe` | Custom startupProbe that overrides the default one. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.resourcesPreset` | Set app container resources according to one common preset (allowed values: none, nano, small, medium, large, xlarge, 2xlarge). This is ignored if app.resources is set (app.resources is recommended for production). | `nano` |
|
||||
| `app.resources` | Set app container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` |
|
||||
| `app.podSecurityContext.enabled` | Enable app pods' Security Context | `true` |
|
||||
@@ -534,6 +735,49 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `app.containerSecurityContext.allowPrivilegeEscalation` | Set allowPrivilegeEscalation in app container' Security Context | `false` |
|
||||
| `app.containerSecurityContext.capabilities.drop` | List of capabilities to be dropped in app container | `["ALL"]` |
|
||||
| `app.containerSecurityContext.seccompProfile.type` | Set seccomp profile in app container | `RuntimeDefault` |
|
||||
| `app.command` | Override default app container command (useful when using custom images) | `[]` |
|
||||
| `app.args` | Override default app container args (useful when using custom images) | `[]` |
|
||||
| `app.automountServiceAccountToken` | Mount Service Account token in app pods. Only effective for helm-deployed apps (see apps.enabled). | `false` |
|
||||
| `app.hostAliases` | app pods host aliases. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.deploymentAnnotations` | Annotations for app deployment. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.podLabels` | Extra labels for app pods. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.podAnnotations` | Annotations for app pods. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.podAffinityPreset` | Pod affinity preset. Ignored if `app.affinity` is set. Allowed values: `soft` or `hard`. Only effective for helm-deployed apps (see apps.enabled). | `""` |
|
||||
| `app.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `app.affinity` is set. Allowed values: `soft` or `hard`. Only effective for helm-deployed apps (see apps.enabled). | `soft` |
|
||||
| `app.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `app.affinity` is set. Allowed values: `soft` or `hard`. Only effective for helm-deployed apps (see apps.enabled). | `""` |
|
||||
| `app.nodeAffinityPreset.key` | Node label key to match. Ignored if `app.affinity` is set | `""` |
|
||||
| `app.nodeAffinityPreset.values` | Node label values to match. Ignored if `app.affinity` is set | `[]` |
|
||||
| `app.affinity` | Affinity for app pods assignment. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.nodeSelector` | Node labels for app pods assignment. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.tolerations` | Tolerations for app pods assignment. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.updateStrategy.type` | app deployment strategy type. Only effective for helm-deployed apps (see apps.enabled). | `RollingUpdate` |
|
||||
| `app.priorityClassName` | app pods' priorityClassName. Only effective for helm-deployed apps (see apps.enabled). | `""` |
|
||||
| `app.topologySpreadConstraints` | Topology Spread Constraints for app pod assignment spread across your cluster among failure-domains. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.schedulerName` | Name of the k8s scheduler (other than default) for app pods. Only effective for helm-deployed apps (see apps.enabled). | `""` |
|
||||
| `app.terminationGracePeriodSeconds` | Seconds app pods need to terminate gracefully. Only effective for helm-deployed apps (see apps.enabled). | `""` |
|
||||
| `app.lifecycleHooks` | for app containers to automate configuration before or after startup. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.extraEnvVars` | Array with extra environment variables to add to app containers. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for app containers. Only effective for helm-deployed apps (see apps.enabled). | `""` |
|
||||
| `app.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for app containers. Only effective for helm-deployed apps (see apps.enabled). | `""` |
|
||||
| `app.extraVolumes` | Optionally specify extra list of additional volumes for the app pods. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the app containers. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.sidecars` | Add additional sidecar containers to the app pods. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.initContainers` | Add additional init containers to the app pods. Only effective for helm-deployed apps (see apps.enabled). | `[]` |
|
||||
| `app.pdb.create` | Enable/disable a Pod Disruption Budget creation. Only effective for helm-deployed apps (see apps.enabled). | `true` |
|
||||
| `app.pdb.minAvailable` | Minimum number/percentage of pods that should remain scheduled | `""` |
|
||||
| `app.pdb.maxUnavailable` | Maximum number/percentage of pods that may be made unavailable. Defaults to `1` if both `app.pdb.minAvailable` and `app.pdb.maxUnavailable` are empty. | `""` |
|
||||
| `app.autoscaling.vpa.enabled` | Enable VPA for app pods. Only effective for helm-deployed apps (see apps.enabled). | `false` |
|
||||
| `app.autoscaling.vpa.annotations` | Annotations for VPA resource | `{}` |
|
||||
| `app.autoscaling.vpa.controlledResources` | VPA List of resources that the vertical pod autoscaler can control. Defaults to cpu and memory | `[]` |
|
||||
| `app.autoscaling.vpa.maxAllowed` | VPA Max allowed resources for the pod | `{}` |
|
||||
| `app.autoscaling.vpa.minAllowed` | VPA Min allowed resources for the pod | `{}` |
|
||||
| `app.autoscaling.vpa.updatePolicy.updateMode` | Autoscaling update policy | `Auto` |
|
||||
| `app.autoscaling.hpa.enabled` | Enable HPA for app pods. Only effective for helm-deployed apps (see apps.enabled). | `false` |
|
||||
| `app.autoscaling.hpa.minReplicas` | Minimum number of replicas | `""` |
|
||||
| `app.autoscaling.hpa.maxReplicas` | Maximum number of replicas | `""` |
|
||||
| `app.autoscaling.hpa.targetCPU` | Target CPU utilization percentage | `""` |
|
||||
| `app.autoscaling.hpa.targetMemory` | Target Memory utilization percentage | `""` |
|
||||
| `app.service.labels` | Extra labels for app service. Only effective for helm-deployed apps (see apps.enabled). | `{}` |
|
||||
| `app.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` |
|
||||
| `app.serviceAccount.name` | The name of the ServiceAccount to use. | `""` |
|
||||
| `app.serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` |
|
||||
@@ -545,7 +789,240 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `app.networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` |
|
||||
| `app.networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` |
|
||||
| `app.networkPolicy.extraEgress` | Add extra ingress rules to the NetworkPolicy (ignored if allowExternalEgress=true) | `[]` |
|
||||
| `app.mountTmpVolume` | Whether a writable /tmp emptyDir volume should be mounted to the app. | `true` |
|
||||
| `app.exposedContainerPort` | The port that shuffle app containers will listen on for new requests. | `80` |
|
||||
| `app.sdkTimeout` | The timeout in seconds for app actions. | `300` |
|
||||
| `app.disableLogs` | Do not capture app logs. By default, app logs are captured, so that they are visible in the frontend. | `false` |
|
||||
|
||||
##### Parameters to deploy apps using helm
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------- | -------------------------------------------------- | ------- |
|
||||
| `apps.enabled` | Whether apps should be deployed using helm. | `false` |
|
||||
| `apps.shuffleTools.enabled` | Whether the shuffle-tools app is enabled | `true` |
|
||||
| `apps.shuffleTools.version` | The version of the shuffle-tools app to deploy. | `1.2.0` |
|
||||
| `apps.shuffleSubflow.enabled` | Whether the shuffle-subflow app is enabled | `true` |
|
||||
| `apps.shuffleSubflow.version` | The version of the shuffle-subflow app to deploy. | `1.1.0` |
|
||||
| `apps.http.enabled` | Whether the http app is enabled | `true` |
|
||||
| `apps.http.version` | The version of the http app to deploy. | `1.4.0` |
|
||||
| `apps.MY_APP.app` | The name of the app (required, e.g. shuffle-tools) | |
|
||||
| `apps.MY_APP.version` | The version of the app (required, e.g. 1.2.0) | |
|
||||
|
||||
##### Traffic Exposure Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------------- | --------------- |
|
||||
| `ingress.enabled` | Enable ingress record generation for frontend and backend | `false` |
|
||||
| `ingress.pathType` | Ingress path type for the frontend path | `Prefix` |
|
||||
| `ingress.backendPathType` | Ingress path type for the backend path | `Prefix` |
|
||||
| `ingress.apiVersion` | Force Ingress API version (automatically detected if not set) | `""` |
|
||||
| `ingress.hostname` | Default host for the ingress record | `shuffle.local` |
|
||||
| `ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `nginx` |
|
||||
| `ingress.path` | Ingress path for Shuffle frontend | `"/"` |
|
||||
| `ingress.backendPath` | Ingress path for Shuffle backend | `"/api/"` |
|
||||
| `ingress.annotations` | Additional annotations for the Ingress resource. | `{}` |
|
||||
| `ingress.tls` | Enable TLS configuration for the host defined at `ingress.hostname` parameter | `false` |
|
||||
| `ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` |
|
||||
| `ingress.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` |
|
||||
| `ingress.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` |
|
||||
| `ingress.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` |
|
||||
| `ingress.secrets` | Custom TLS certificates as secrets | `[]` |
|
||||
| `ingress.extraRules` | Additional rules to be covered with this ingress record | `[]` |
|
||||
|
||||
##### Istio Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| --------------------------------------- | ------------------------------------------------------------------------------- | ------------------------ |
|
||||
| `istio.enabled` | Enable creation of an Istio Gateway and VirtualService for frontend and backend | `false` |
|
||||
| `istio.apiVersion` | The istio apiVersion to use for Gateway and VirtualService resources | `networking.istio.io/v1` |
|
||||
| `istio.hosts` | One or more hosts exposed by Istio | `[]` |
|
||||
| `istio.gateway.annotations` | Additional annotations for the Gateway resource | `{}` |
|
||||
| `istio.gateway.selector` | The selector matches the ingress gateway pod labels | `{ istio: ingress }` |
|
||||
| `istio.gateway.http.enabled` | Enable HTTP server port 80 | `true` |
|
||||
| `istio.gateway.http.httpsRedirect` | If set to true, a 301 redirect is send for all HTTP connections | `false` |
|
||||
| `istio.gateway.https.enabled` | Enable HTTPS server on port 443 | `false` |
|
||||
| `istio.gateway.https.tlsCredentialName` | The name of the secret that holds the TLS certs including the CA certificates. | `""` |
|
||||
| `istio.gateway.https.tlsCipherSuites` | If specified, only support the specified cipher list. | `[]` |
|
||||
| `istio.gateway.extraServers` | Additional servers for the Gateway resource | `[]` |
|
||||
| `istio.virtualService.annotations` | Additional annotations for the VirtualService resource. | `{}` |
|
||||
| `istio.virtualService.backendHeaders` | Header manipulation rules for backend traffic | `{}` |
|
||||
| `istio.virtualService.frontendHeaders` | Header manipulation rules for frontend traffic | `{}` |
|
||||
|
||||
##### Persistence Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------- | ------------------------------------------------- | ------------------- |
|
||||
| `persistence.enabled` | Enable persistence using Persistent Volume Claims | `true` |
|
||||
| `persistence.apps.existingClaim` | Name of an existing PVC to use | `""` |
|
||||
| `persistence.apps.storageClass` | PVC Storage Class for shuffle-apps volume | `""` |
|
||||
| `persistence.apps.subPath` | The sub path used in the volume | `""` |
|
||||
| `persistence.apps.accessModes` | The access mode of the volume | `["ReadWriteOnce"]` |
|
||||
| `persistence.apps.size` | The size of the volume | `5Gi` |
|
||||
| `persistence.apps.annotations` | Annotations for the PVC | `{}` |
|
||||
| `persistence.apps.selector` | Selector to match an existing Persistent Volume | `{}` |
|
||||
| `persistence.appBuilder.storageClass` | PVC Storage Class for backend-apps-claim volume | `""` |
|
||||
| `persistence.appBuilder.accessModes` | The access mode of the volume | `["ReadWriteOnce"]` |
|
||||
| `persistence.appBuilder.size` | The size of the volume | `5Gi` |
|
||||
| `persistence.appBuilder.annotations` | Annotations for the PVC | `{}` |
|
||||
| `persistence.appBuilder.selector` | Selector to match an existing Persistent Volume | `{}` |
|
||||
| `persistence.files.existingClaim` | Name of an existing PVC to use | `""` |
|
||||
| `persistence.files.storageClass` | PVC Storage Class for shuffle-files volume | `""` |
|
||||
| `persistence.files.subPath` | The sub path used in the volume | `""` |
|
||||
| `persistence.files.accessModes` | The access mode of the volume | `["ReadWriteOnce"]` |
|
||||
| `persistence.files.size` | The size of the volume | `5Gi` |
|
||||
| `persistence.files.annotations` | Annotations for the PVC | `{}` |
|
||||
| `persistence.files.selector` | Selector to match an existing Persistent Volume | `{}` |
|
||||
|
||||
##### Init Container Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
||||
| `volumePermissions.enabled` | Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` | `false` |
|
||||
| `volumePermissions.image.registry` | OS Shell + Utility image registry | `docker.io` |
|
||||
| `volumePermissions.image.repository` | OS Shell + Utility image repository | `bitnamilegacy/os-shell` |
|
||||
| `volumePermissions.image.tag` | OS Shell + Utility image tag (immutable tags are recommended) | `12-debian-12-r30` |
|
||||
| `volumePermissions.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` |
|
||||
| `volumePermissions.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` |
|
||||
| `volumePermissions.resourcesPreset` | Set init container resources according to one common preset (allowed values: none, nano, small, medium, large, xlarge, 2xlarge). This is ignored if volumePermissions.resources is set (volumePermissions.resources is recommended for production). | `nano` |
|
||||
| `volumePermissions.resources` | Set init container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.enabled` | Enabled init container' Security Context | `true` |
|
||||
| `volumePermissions.containerSecurityContext.seLinuxOptions` | Set SELinux options in init container | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.runAsUser` | Set init container's Security Context runAsUser | `0` |
|
||||
|
||||
##### OpenSearch Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------------------- | ----------------------------------------------------- | ------ |
|
||||
| `opensearch.enabled` | Switch to enable or disable the opensearch helm chart | `true` |
|
||||
|
||||
##### Vault Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| --------------- | -------------------------------------------------------------------------- | ----- |
|
||||
| `vault.role` | Specify the Vault role, which should be used to get the secret from Vault. | `""` |
|
||||
| `vault.secrets` | A list of VaultSecrets to create | `[]` |
|
||||
|
||||
##### Other Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------- | -------------------------------------------------- | ------- |
|
||||
| `apps.enabled` | Whether apps should be deployed using helm. | `false` |
|
||||
| `apps.shuffleTools.enabled` | Whether the shuffle-tools app is enabled | `true` |
|
||||
| `apps.shuffleTools.version` | The version of the shuffle-tools app to deploy. | `1.2.0` |
|
||||
| `apps.shuffleSubflow.enabled` | Whether the shuffle-subflow app is enabled | `true` |
|
||||
| `apps.shuffleSubflow.version` | The version of the shuffle-subflow app to deploy. | `1.1.0` |
|
||||
| `apps.http.enabled` | Whether the http app is enabled | `true` |
|
||||
| `apps.http.version` | The version of the http app to deploy. | `1.4.0` |
|
||||
| `apps.MY_APP.app` | The name of the app (required, e.g. shuffle-tools) | |
|
||||
| `apps.MY_APP.version` | The version of the app (required, e.g. 1.2.0) | |
|
||||
|
||||
#### Traffic Exposure Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------------- | --------------- |
|
||||
| `ingress.enabled` | Enable ingress record generation for frontend and backend | `false` |
|
||||
| `ingress.pathType` | Ingress path type for the frontend path | `Prefix` |
|
||||
| `ingress.backendPathType` | Ingress path type for the backend path | `Prefix` |
|
||||
| `ingress.apiVersion` | Force Ingress API version (automatically detected if not set) | `""` |
|
||||
| `ingress.hostname` | Default host for the ingress record | `shuffle.local` |
|
||||
| `ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `nginx` |
|
||||
| `ingress.path` | Ingress path for Shuffle frontend | `"/"` |
|
||||
| `ingress.backendPath` | Ingress path for Shuffle backend | `"/api/"` |
|
||||
| `ingress.annotations` | Additional annotations for the Ingress resource. | `{}` |
|
||||
| `ingress.tls` | Enable TLS configuration for the host defined at `ingress.hostname` parameter | `false` |
|
||||
| `ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` |
|
||||
| `ingress.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` |
|
||||
| `ingress.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` |
|
||||
| `ingress.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` |
|
||||
| `ingress.secrets` | Custom TLS certificates as secrets | `[]` |
|
||||
| `ingress.extraRules` | Additional rules to be covered with this ingress record | `[]` |
|
||||
|
||||
#### Istio Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| --------------------------------------- | ------------------------------------------------------------------------------- | ------------------------ |
|
||||
| `istio.enabled` | Enable creation of an Istio Gateway and VirtualService for frontend and backend | `false` |
|
||||
| `istio.apiVersion` | The istio apiVersion to use for Gateway and VirtualService resources | `networking.istio.io/v1` |
|
||||
| `istio.hosts` | One or more hosts exposed by Istio | `[]` |
|
||||
| `istio.gateway.annotations` | Additional annotations for the Gateway resource | `{}` |
|
||||
| `istio.gateway.selector` | The selector matches the ingress gateway pod labels | `{ istio: ingress }` |
|
||||
| `istio.gateway.http.enabled` | Enable HTTP server port 80 | `true` |
|
||||
| `istio.gateway.http.httpsRedirect` | If set to true, a 301 redirect is send for all HTTP connections | `false` |
|
||||
| `istio.gateway.https.enabled` | Enable HTTPS server on port 443 | `false` |
|
||||
| `istio.gateway.https.tlsCredentialName` | The name of the secret that holds the TLS certs including the CA certificates. | `""` |
|
||||
| `istio.gateway.https.tlsCipherSuites` | If specified, only support the specified cipher list. | `[]` |
|
||||
| `istio.gateway.extraServers` | Additional servers for the Gateway resource | `[]` |
|
||||
| `istio.virtualService.annotations` | Additional annotations for the VirtualService resource. | `{}` |
|
||||
| `istio.virtualService.backendHeaders` | Header manipulation rules for backend traffic | `{}` |
|
||||
| `istio.virtualService.frontendHeaders` | Header manipulation rules for frontend traffic | `{}` |
|
||||
|
||||
#### Persistence Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------- | ------------------------------------------------- | ------------------- |
|
||||
| `persistence.enabled` | Enable persistence using Persistent Volume Claims | `true` |
|
||||
| `persistence.apps.existingClaim` | Name of an existing PVC to use | `""` |
|
||||
| `persistence.apps.storageClass` | PVC Storage Class for shuffle-apps volume | `""` |
|
||||
| `persistence.apps.subPath` | The sub path used in the volume | `""` |
|
||||
| `persistence.apps.accessModes` | The access mode of the volume | `["ReadWriteOnce"]` |
|
||||
| `persistence.apps.size` | The size of the volume | `5Gi` |
|
||||
| `persistence.apps.annotations` | Annotations for the PVC | `{}` |
|
||||
| `persistence.apps.selector` | Selector to match an existing Persistent Volume | `{}` |
|
||||
| `persistence.appBuilder.storageClass` | PVC Storage Class for backend-apps-claim volume | `""` |
|
||||
| `persistence.appBuilder.accessModes` | The access mode of the volume | `["ReadWriteOnce"]` |
|
||||
| `persistence.appBuilder.size` | The size of the volume | `5Gi` |
|
||||
| `persistence.appBuilder.annotations` | Annotations for the PVC | `{}` |
|
||||
| `persistence.appBuilder.selector` | Selector to match an existing Persistent Volume | `{}` |
|
||||
| `persistence.files.existingClaim` | Name of an existing PVC to use | `""` |
|
||||
| `persistence.files.storageClass` | PVC Storage Class for shuffle-files volume | `""` |
|
||||
| `persistence.files.subPath` | The sub path used in the volume | `""` |
|
||||
| `persistence.files.accessModes` | The access mode of the volume | `["ReadWriteOnce"]` |
|
||||
| `persistence.files.size` | The size of the volume | `5Gi` |
|
||||
| `persistence.files.annotations` | Annotations for the PVC | `{}` |
|
||||
| `persistence.files.selector` | Selector to match an existing Persistent Volume | `{}` |
|
||||
|
||||
#### Init Container Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
||||
| `volumePermissions.enabled` | Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` | `false` |
|
||||
| `volumePermissions.image.registry` | OS Shell + Utility image registry | `docker.io` |
|
||||
| `volumePermissions.image.repository` | OS Shell + Utility image repository | `bitnamilegacy/os-shell` |
|
||||
| `volumePermissions.image.tag` | OS Shell + Utility image tag (immutable tags are recommended) | `12-debian-12-r30` |
|
||||
| `volumePermissions.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` |
|
||||
| `volumePermissions.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` |
|
||||
| `volumePermissions.resourcesPreset` | Set init container resources according to one common preset (allowed values: none, nano, small, medium, large, xlarge, 2xlarge). This is ignored if volumePermissions.resources is set (volumePermissions.resources is recommended for production). | `nano` |
|
||||
| `volumePermissions.resources` | Set init container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.enabled` | Enabled init container' Security Context | `true` |
|
||||
| `volumePermissions.containerSecurityContext.seLinuxOptions` | Set SELinux options in init container | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.runAsUser` | Set init container's Security Context runAsUser | `0` |
|
||||
|
||||
#### OpenSearch Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------------------- | ----------------------------------------------------- | ------ |
|
||||
| `opensearch.enabled` | Switch to enable or disable the opensearch helm chart | `true` |
|
||||
|
||||
#### Vault Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| --------------- | -------------------------------------------------------------------------- | ----- |
|
||||
| `vault.role` | Specify the Vault role, which should be used to get the secret from Vault. | `""` |
|
||||
| `vault.secrets` | A list of VaultSecrets to create | `[]` |
|
||||
|
||||
#### Other Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------- | -------------------------------------------------- | ------- |
|
||||
| `apps.enabled` | Whether apps should be deployed using helm. | `false` |
|
||||
| `apps.shuffleTools.enabled` | Whether the shuffle-tools app is enabled | `true` |
|
||||
| `apps.shuffleTools.version` | The version of the shuffle-tools app to deploy. | `1.2.0` |
|
||||
| `apps.shuffleSubflow.enabled` | Whether the shuffle-subflow app is enabled | `true` |
|
||||
| `apps.shuffleSubflow.version` | The version of the shuffle-subflow app to deploy. | `1.1.0` |
|
||||
| `apps.http.enabled` | Whether the http app is enabled | `true` |
|
||||
| `apps.http.version` | The version of the http app to deploy. | `1.4.0` |
|
||||
| `apps.MY_APP.app` | The name of the app (required, e.g. shuffle-tools) | |
|
||||
| `apps.MY_APP.version` | The version of the app (required, e.g. 1.2.0) | |
|
||||
|
||||
### Traffic Exposure Parameters
|
||||
|
||||
@@ -614,19 +1091,19 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
|
||||
### Init Container Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
||||
| `volumePermissions.enabled` | Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` | `false` |
|
||||
| `volumePermissions.image.registry` | OS Shell + Utility image registry | `docker.io` |
|
||||
| `volumePermissions.image.repository` | OS Shell + Utility image repository | `bitnami/os-shell` |
|
||||
| `volumePermissions.image.tag` | OS Shell + Utility image tag (immutable tags are recommended) | `12-debian-12-r30` |
|
||||
| `volumePermissions.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` |
|
||||
| `volumePermissions.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` |
|
||||
| `volumePermissions.resourcesPreset` | Set init container resources according to one common preset (allowed values: none, nano, small, medium, large, xlarge, 2xlarge). This is ignored if volumePermissions.resources is set (volumePermissions.resources is recommended for production). | `nano` |
|
||||
| `volumePermissions.resources` | Set init container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.enabled` | Enabled init container' Security Context | `true` |
|
||||
| `volumePermissions.containerSecurityContext.seLinuxOptions` | Set SELinux options in init container | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.runAsUser` | Set init container's Security Context runAsUser | `0` |
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
||||
| `volumePermissions.enabled` | Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` | `false` |
|
||||
| `volumePermissions.image.registry` | OS Shell + Utility image registry | `docker.io` |
|
||||
| `volumePermissions.image.repository` | OS Shell + Utility image repository | `bitnamilegacy/os-shell` |
|
||||
| `volumePermissions.image.tag` | OS Shell + Utility image tag (immutable tags are recommended) | `12-debian-12-r30` |
|
||||
| `volumePermissions.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` |
|
||||
| `volumePermissions.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` |
|
||||
| `volumePermissions.resourcesPreset` | Set init container resources according to one common preset (allowed values: none, nano, small, medium, large, xlarge, 2xlarge). This is ignored if volumePermissions.resources is set (volumePermissions.resources is recommended for production). | `nano` |
|
||||
| `volumePermissions.resources` | Set init container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.enabled` | Enabled init container' Security Context | `true` |
|
||||
| `volumePermissions.containerSecurityContext.seLinuxOptions` | Set SELinux options in init container | `{}` |
|
||||
| `volumePermissions.containerSecurityContext.runAsUser` | Set init container's Security Context runAsUser | `0` |
|
||||
|
||||
### OpenSearch Parameters
|
||||
|
||||
@@ -642,6 +1119,3 @@ The password should be provided with the `SHUFFLE_OPENSEARCH_PASSWORD` env varia
|
||||
| `vault.secrets` | A list of VaultSecrets to create | `[]` |
|
||||
|
||||
### Other Parameters
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,6 @@ Access the pod you want to debug by executing
|
||||
|
||||
To access shuffle using port-forwarding:
|
||||
|
||||
1. Run `kubectl port-forward -n shuffle svc/shuffle-frontend 8080:http`
|
||||
1. Run `kubectl port-forward -n {{ .Release.Namespace }} svc/{{ include "shuffle.frontend.name" . }} 8080:http`
|
||||
2. Visit http://localhost:8080 with your browser
|
||||
|
||||
3. Create the administrator account, if not already done
|
||||
|
||||
@@ -1,377 +1,6 @@
|
||||
{{/*
|
||||
Return the common name for backend componentes
|
||||
*/}}
|
||||
{{- define "shuffle.backend.name" -}}
|
||||
{{- printf "%s-backend" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common name for frontend components
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.name" -}}
|
||||
{{- printf "%s-frontend" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common name for orborus components
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.name" -}}
|
||||
{{- printf "%s-orborus" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common name for worker components
|
||||
*/}}
|
||||
{{- define "shuffle.worker.name" -}}
|
||||
{{- printf "%s-worker" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common name for app components
|
||||
*/}}
|
||||
{{- define "shuffle.app.name" -}}
|
||||
{{- printf "%s-app" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for backend components
|
||||
The shuffle app builder requires the io.kompose.service=backend label to be set on the backend pod.
|
||||
*/}}
|
||||
{{- define "shuffle.backend.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: backend
|
||||
io.kompose.service: backend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for frontend components
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: frontend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for orborus components
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: orborus
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for worker components
|
||||
*/}}
|
||||
{{- define "shuffle.worker.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: worker
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for app components
|
||||
*/}}
|
||||
{{- define "shuffle.app.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: app
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for backend components
|
||||
*/}}
|
||||
{{- define "shuffle.backend.matchLabels" -}}
|
||||
{{- include "common.labels.matchLabels" . }}
|
||||
app.kubernetes.io/component: backend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for frontend components
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.matchLabels" -}}
|
||||
{{- include "common.labels.matchLabels" . }}
|
||||
app.kubernetes.io/component: frontend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for orborus components
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.matchLabels" -}}
|
||||
{{- include "common.labels.matchLabels" . }}
|
||||
app.kubernetes.io/component: orborus
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for worker components
|
||||
NOTE: This does not match the labels from shuffle.worker.labels, but the labels set by the orborus GoLang app.
|
||||
*/}}
|
||||
{{- define "shuffle.worker.matchLabels" -}}
|
||||
app.kubernetes.io/name: shuffle-worker
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for app components
|
||||
NOTE: This does not match the labels from shuffle.worker.labels, but the labels set by the orborus GoLang app.
|
||||
*/}}
|
||||
{{- define "shuffle.app.matchLabels" -}}
|
||||
app.kubernetes.io/name: shuffle-app
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper image name (for the init container volume-permissions image)
|
||||
*/}}
|
||||
{{- define "shuffle.volumePermissions.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.volumePermissions.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Shuffle backend image name
|
||||
*/}}
|
||||
{{- define "shuffle.backend.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.backend.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the backend pod
|
||||
*/}}
|
||||
{{- define "shuffle.backend.imagePullSecrets" -}}
|
||||
{{- include "common.images.renderPullSecrets" (dict "images" (list .Values.backend.image) "context" $) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Shuffle frontend image name
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.frontend.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the frontend pod
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.imagePullSecrets" -}}
|
||||
{{- include "common.images.renderPullSecrets" (dict "images" (list .Values.frontend.image) "context" $) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Shuffle orborus image name
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.orborus.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the orborus pod
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.imagePullSecrets" -}}
|
||||
{{- include "common.images.renderPullSecrets" (dict "images" (list .Values.orborus.image) "context" $) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Shuffle worker image name
|
||||
*/}}
|
||||
{{- define "shuffle.worker.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.worker.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for the Shuffle backend
|
||||
*/}}
|
||||
{{- define "shuffle.backend.serviceAccount.name" -}}
|
||||
{{- if .Values.backend.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.backend.name" .) .Values.backend.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.backend.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the backend service account
|
||||
*/}}
|
||||
{{- define "shuffle.backend.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.backend.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for the Shuffle frontend
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.serviceAccount.name" -}}
|
||||
{{- if .Values.frontend.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.frontend.name" .) .Values.frontend.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.frontend.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the frontend service account
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.frontend.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for Shuffle orborus
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.serviceAccount.name" -}}
|
||||
{{- if .Values.orborus.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.orborus.name" .) .Values.orborus.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.orborus.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the orborus service account
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.orborus.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for Shuffle workers
|
||||
*/}}
|
||||
{{- define "shuffle.worker.serviceAccount.name" -}}
|
||||
{{- if .Values.worker.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.worker.name" .) .Values.worker.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.worker.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the worker service account
|
||||
*/}}
|
||||
{{- define "shuffle.worker.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.worker.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for Shuffle apps
|
||||
*/}}
|
||||
{{- define "shuffle.app.serviceAccount.name" -}}
|
||||
{{- if .Values.app.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.app.name" .) .Values.app.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.app.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the app service account
|
||||
*/}}
|
||||
{{- define "shuffle.app.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.app.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
{{/*
|
||||
Return the common name for backend componentes
|
||||
*/}}
|
||||
{{- define "shuffle.backend.name" -}}
|
||||
{{- printf "%s-backend" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for backend components
|
||||
The shuffle app builder requires the io.kompose.service=backend label to be set on the backend pod.
|
||||
*/}}
|
||||
{{- define "shuffle.backend.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: backend
|
||||
io.kompose.service: backend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for backend components
|
||||
*/}}
|
||||
{{- define "shuffle.backend.matchLabels" -}}
|
||||
{{- include "common.labels.matchLabels" . }}
|
||||
app.kubernetes.io/component: backend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Shuffle backend image name
|
||||
*/}}
|
||||
{{- define "shuffle.backend.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.backend.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the backend pod
|
||||
*/}}
|
||||
{{- define "shuffle.backend.imagePullSecrets" -}}
|
||||
{{- include "common.images.renderPullSecrets" (dict "images" (list .Values.backend.image) "context" $) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for the Shuffle backend
|
||||
*/}}
|
||||
{{- define "shuffle.backend.serviceAccount.name" -}}
|
||||
{{- if .Values.backend.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.backend.name" .) .Values.backend.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.backend.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the backend service account
|
||||
*/}}
|
||||
{{- define "shuffle.backend.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.backend.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "shuffle.backend.baseUrl" -}}
|
||||
http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.backend.containerPorts.http }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the environment variables of shuffle-backend in the format
|
||||
KEY: VALUE
|
||||
*/}}
|
||||
{{- define "shuffle.backend.env" -}}
|
||||
RUNNING_MODE: kubernetes
|
||||
IS_KUBERNETES: "true"
|
||||
SHUFFLE_APP_HOTLOAD_FOLDER: /shuffle-apps
|
||||
SHUFFLE_FILE_LOCATION: /shuffle-files
|
||||
BACKEND_PORT: "{{ .Values.backend.containerPorts.http }}"
|
||||
{{- if .Values.shuffle.baseUrl }}
|
||||
BASE_URL: "{{ .Values.shuffle.baseUrl }}"
|
||||
SSO_REDIRECT_URL: "{{ .Values.shuffle.baseUrl }}"
|
||||
{{- else }}
|
||||
BASE_URL: "{{ include "shuffle.backend.baseUrl" . }}"
|
||||
{{- end }}
|
||||
ORG_ID: "{{ .Values.shuffle.org }}"
|
||||
SHUFFLE_APP_DOWNLOAD_LOCATION: "{{ .Values.backend.apps.downloadLocation }}"
|
||||
SHUFFLE_DOWNLOAD_AUTH_BRANCH: "{{ .Values.backend.apps.downloadBranch }}"
|
||||
SHUFFLE_APP_FORCE_UPDATE: "{{ .Values.backend.apps.forceUpdate }}"
|
||||
SHUFFLE_CHAT_DISABLED: "true"
|
||||
# Sets backend_url parameter for workflow execution to the cluster-internal shuffle-backend address
|
||||
SHUFFLE_CLOUDRUN_URL: "{{ include "shuffle.backend.baseUrl" .}}"
|
||||
SHUFFLE_OPENSEARCH_URL: {{ include "common.tplvalues.render" (dict "value" .Values.backend.openSearch.url "context" $) }}
|
||||
SHUFFLE_OPENSEARCH_USERNAME: "{{ .Values.backend.openSearch.username }}"
|
||||
SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: "{{ .Values.backend.openSearch.certificateFile }}"
|
||||
SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: "{{ .Values.backend.openSearch.skipSSLVerify }}"
|
||||
SHUFFLE_OPENSEARCH_INDEX_PREFIX: "{{ .Values.backend.openSearch.indexPrefix }}"
|
||||
SHUFFLE_RERUN_SCHEDULE: "{{ .Values.backend.cleanupSchedule }}"
|
||||
TZ: "{{ .Values.shuffle.timezone }}"
|
||||
REGISTRY_URL: "{{ .Values.shuffle.appRegistry }}" # Used by app builder
|
||||
{{- end -}}
|
||||
@@ -1,32 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "shuffle.backend.name" . }}-env
|
||||
namespace: {{ include "common.names.namespace" . | quote }}
|
||||
labels: {{- include "shuffle.backend.labels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }}
|
||||
{{- if .Values.commonAnnotations }}
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
data:
|
||||
BACKEND_PORT: "{{ .Values.backend.containerPorts.http }}"
|
||||
{{- if .Values.shuffle.baseUrl }}
|
||||
BASE_URL: "{{ .Values.shuffle.baseUrl }}"
|
||||
SSO_REDIRECT_URL: "{{ .Values.shuffle.baseUrl }}"
|
||||
{{- else }}
|
||||
BASE_URL: "http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.backend.containerPorts.http }}"
|
||||
{{- end }}
|
||||
ORG_ID: "{{ .Values.shuffle.org }}"
|
||||
SHUFFLE_APP_DOWNLOAD_LOCATION: "{{ .Values.backend.apps.downloadLocation }}"
|
||||
SHUFFLE_DOWNLOAD_AUTH_BRANCH: "{{ .Values.backend.apps.downloadBranch }}"
|
||||
SHUFFLE_APP_FORCE_UPDATE: "{{ .Values.backend.apps.forceUpdate }}"
|
||||
SHUFFLE_CHAT_DISABLED: "true"
|
||||
# Sets backend_url parameter for workflow execution to the cluster-internal shuffle-backend address
|
||||
SHUFFLE_CLOUDRUN_URL: "http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.backend.containerPorts.http }}"
|
||||
SHUFFLE_OPENSEARCH_URL: {{ include "common.tplvalues.render" (dict "value" .Values.backend.openSearch.url "context" $) }}
|
||||
SHUFFLE_OPENSEARCH_USERNAME: "{{ .Values.backend.openSearch.username }}"
|
||||
SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: "{{ .Values.backend.openSearch.certificateFile }}"
|
||||
SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: "{{ .Values.backend.openSearch.skipSSLVerify }}"
|
||||
SHUFFLE_OPENSEARCH_INDEX_PREFIX: "{{ .Values.backend.openSearch.indexPrefix }}"
|
||||
SHUFFLE_RERUN_SCHEDULE: "{{ .Values.backend.cleanupSchedule }}"
|
||||
TZ: "{{ .Values.shuffle.timezone }}"
|
||||
REGISTRY_URL: "{{ .Values.shuffle.appRegistry }}"
|
||||
@@ -115,20 +115,15 @@ spec:
|
||||
args: {{- include "common.tplvalues.render" (dict "value" .Values.backend.args "context" $) | nindent 12 }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: RUNNING_MODE
|
||||
value: kubernetes
|
||||
- name: IS_KUBERNETES
|
||||
value: "true"
|
||||
- name: SHUFFLE_APP_HOTLOAD_FOLDER
|
||||
value: /shuffle-apps
|
||||
- name: SHUFFLE_FILE_LOCATION
|
||||
value: /shuffle-files
|
||||
{{- $env := include "shuffle.backend.env" . | fromYaml }}
|
||||
{{- range $key, $val := $env }}
|
||||
- name: {{ $key | quote }}
|
||||
value: {{ $val | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.backend.extraEnvVars }}
|
||||
{{- include "common.tplvalues.render" (dict "value" .Values.backend.extraEnvVars "context" $) | nindent 12 }}
|
||||
{{- end }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "shuffle.backend.name" . }}-env
|
||||
{{- if .Values.backend.extraEnvVarsCM }}
|
||||
- configMapRef:
|
||||
name: {{ include "common.tplvalues.render" (dict "value" .Values.backend.extraEnvVarsCM "context" $) }}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
{{/*
|
||||
Return the common name for frontend components
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.name" -}}
|
||||
{{- printf "%s-frontend" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for frontend components
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: frontend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for frontend components
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.matchLabels" -}}
|
||||
{{- include "common.labels.matchLabels" . }}
|
||||
app.kubernetes.io/component: frontend
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Shuffle frontend image name
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.frontend.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the frontend pod
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.imagePullSecrets" -}}
|
||||
{{- include "common.images.renderPullSecrets" (dict "images" (list .Values.frontend.image) "context" $) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for the Shuffle frontend
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.serviceAccount.name" -}}
|
||||
{{- if .Values.frontend.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.frontend.name" .) .Values.frontend.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.frontend.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the frontend service account
|
||||
*/}}
|
||||
{{- define "shuffle.frontend.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.frontend.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,128 @@
|
||||
{{/*
|
||||
Return the common name for orborus components
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.name" -}}
|
||||
{{- printf "%s-orborus" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for orborus components
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: orborus
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for orborus components
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.matchLabels" -}}
|
||||
{{- include "common.labels.matchLabels" . }}
|
||||
app.kubernetes.io/component: orborus
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Shuffle orborus image name
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.image" -}}
|
||||
{{- include "common.images.image" ( dict "imageRoot" .Values.orborus.image "global" .Values.global "chart" .Chart ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the orborus pod
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.imagePullSecrets" -}}
|
||||
{{- include "common.images.renderPullSecrets" (dict "images" (list .Values.orborus.image) "context" $) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for Shuffle orborus
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.serviceAccount.name" -}}
|
||||
{{- if .Values.orborus.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.orborus.name" .) .Values.orborus.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.orborus.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the orborus service account
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.orborus.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the environment variables of shuffle-orborus in the format
|
||||
KEY: VALUE
|
||||
*/}}
|
||||
{{- define "shuffle.orborus.env" -}}
|
||||
RUNNING_MODE: kubernetes
|
||||
IS_KUBERNETES: "true"
|
||||
ENVIRONMENT_NAME: "{{ .Values.shuffle.org }}"
|
||||
ORG_ID: "{{ .Values.shuffle.org }}"
|
||||
TZ: "{{ .Values.shuffle.timezone }}"
|
||||
BASE_URL: {{ include "shuffle.backend.baseUrl" . | quote }}
|
||||
KUBERNETES_NAMESPACE: "{{ .Release.Namespace }}"
|
||||
SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY: {{ .Values.orborus.executionConcurrency | quote }}
|
||||
|
||||
{{- if .Values.orborus.manageWorkerDeployments }}
|
||||
# Shuffle worker configuration
|
||||
SHUFFLE_WORKER_IMAGE: {{ include "shuffle.worker.image" . | quote }}
|
||||
SHUFFLE_WORKER_SERVICE_ACCOUNT_NAME: {{ include "shuffle.worker.serviceAccount.name" . | quote }}
|
||||
{{- if .Values.worker.podSecurityContext.enabled }}
|
||||
SHUFFLE_WORKER_POD_SECURITY_CONTEXT: {{ omit .Values.worker.podSecurityContext "enabled" | mustToJson | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.worker.containerSecurityContext.enabled }}
|
||||
SHUFFLE_WORKER_CONTAINER_SECURITY_CONTEXT: {{ include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.worker.containerSecurityContext "context" $) | fromYaml | mustToJson | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Shuffle worker resources
|
||||
{{- $workerResources := (.Values.worker.resources | default (include "common.resources.preset" (dict "type" .Values.worker.resourcesPreset) | fromYaml)) -}}
|
||||
{{- if and $workerResources.requests $workerResources.requests.cpu }}
|
||||
SHUFFLE_WORKER_CPU_REQUEST: {{ $workerResources.requests.cpu | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.requests $workerResources.requests.memory}}
|
||||
SHUFFLE_WORKER_MEMORY_REQUEST: {{ $workerResources.requests.memory | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.requests (index $workerResources.requests "ephemeral-storage") }}
|
||||
SHUFFLE_WORKER_EPHEMERAL_STORAGE_REQUEST: {{ (index $workerResources.requests "ephemeral-storage") | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.limits $workerResources.limits.cpu }}
|
||||
SHUFFLE_WORKER_CPU_LIMIT: {{ $workerResources.limits.cpu | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.limits $workerResources.limits.memory}}
|
||||
SHUFFLE_WORKER_MEMORY_LIMIT: {{ $workerResources.limits.memory | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.limits (index $workerResources.limits "ephemeral-storage") }}
|
||||
SHUFFLE_WORKER_EPHEMERAL_STORAGE_LIMIT: {{ (index $workerResources.limits "ephemeral-storage") | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Include shuffle worker environment variables. Orborus passes them down to worker, when creating the deployment.
|
||||
{{ include "shuffle.workerInstance.env" . }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
@@ -1,78 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "shuffle.orborus.name" . }}-env
|
||||
namespace: {{ include "common.names.namespace" . | quote }}
|
||||
labels: {{- include "shuffle.orborus.labels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }}
|
||||
{{- if .Values.commonAnnotations }}
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
data:
|
||||
ENVIRONMENT_NAME: "{{ .Values.shuffle.org }}"
|
||||
ORG_ID: "{{ .Values.shuffle.org }}"
|
||||
TZ: "{{ .Values.shuffle.timezone }}"
|
||||
BASE_URL: "http://{{ include "shuffle.backend.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.backend.containerPorts.http }}"
|
||||
KUBERNETES_NAMESPACE: "{{ .Release.Namespace }}"
|
||||
REGISTRY_URL: "{{ .Values.shuffle.appRegistry }}"
|
||||
|
||||
# Shuffle worker configuration
|
||||
SHUFFLE_WORKER_IMAGE: {{ include "shuffle.worker.image" . | quote }}
|
||||
SHUFFLE_WORKER_SERVICE_ACCOUNT_NAME: {{ include "shuffle.worker.serviceAccount.name" . | quote }}
|
||||
{{- if .Values.worker.podSecurityContext.enabled }}
|
||||
SHUFFLE_WORKER_POD_SECURITY_CONTEXT: {{ omit .Values.worker.podSecurityContext "enabled" | mustToJson | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.worker.containerSecurityContext.enabled }}
|
||||
SHUFFLE_WORKER_CONTAINER_SECURITY_CONTEXT: {{ include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.worker.containerSecurityContext "context" $) | fromYaml | mustToJson | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Shuffle worker resources
|
||||
{{- $workerResources := (.Values.worker.resources | default (include "common.resources.preset" (dict "type" .Values.worker.resourcesPreset) | fromYaml)) -}}
|
||||
{{- if and $workerResources.requests $workerResources.requests.cpu }}
|
||||
SHUFFLE_WORKER_CPU_REQUEST: {{ $workerResources.requests.cpu | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.requests $workerResources.requests.memory}}
|
||||
SHUFFLE_WORKER_MEMORY_REQUEST: {{ $workerResources.requests.memory | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.requests (index $workerResources.requests "ephemeral-storage") }}
|
||||
SHUFFLE_WORKER_EPHEMERAL_STORAGE_REQUEST: {{ (index $workerResources.requests "ephemeral-storage") | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.limits $workerResources.limits.cpu }}
|
||||
SHUFFLE_WORKER_CPU_LIMIT: {{ $workerResources.limits.cpu | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.limits $workerResources.limits.memory}}
|
||||
SHUFFLE_WORKER_MEMORY_LIMIT: {{ $workerResources.limits.memory | quote }}
|
||||
{{- end }}
|
||||
{{- if and $workerResources.limits (index $workerResources.limits "ephemeral-storage") }}
|
||||
SHUFFLE_WORKER_EPHEMERAL_STORAGE_LIMIT: {{ (index $workerResources.limits "ephemeral-storage") | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Shuffle app configuration
|
||||
SHUFFLE_APP_EXPOSED_PORT: {{ .Values.app.exposedContainerPort | quote }}
|
||||
SHUFFLE_APP_SERVICE_ACCOUNT_NAME: {{ include "shuffle.app.serviceAccount.name" . | quote }}
|
||||
{{- if .Values.app.podSecurityContext.enabled }}
|
||||
SHUFFLE_APP_POD_SECURITY_CONTEXT: {{ omit .Values.app.podSecurityContext "enabled" | mustToJson | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.app.containerSecurityContext.enabled }}
|
||||
SHUFFLE_APP_CONTAINER_SECURITY_CONTEXT: {{ include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.app.containerSecurityContext "context" $) | fromYaml | mustToJson | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Shuffle app resources
|
||||
{{- $appResources := (.Values.app.resources | default (include "common.resources.preset" (dict "type" .Values.app.resourcesPreset) | fromYaml)) -}}
|
||||
{{- if and $appResources.requests $appResources.requests.cpu }}
|
||||
SHUFFLE_APP_CPU_REQUEST: {{ $appResources.requests.cpu | quote }}
|
||||
{{- end }}
|
||||
{{- if and $appResources.requests $appResources.requests.memory }}
|
||||
SHUFFLE_APP_MEMORY_REQUEST: {{ $appResources.requests.memory | quote }}
|
||||
{{- end }}
|
||||
{{- if and $appResources.requests (index $appResources.requests "ephemeral-storage") }}
|
||||
SHUFFLE_APP_EPHEMERAL_STORAGE_REQUEST: {{ (index $appResources.requests "ephemeral-storage") | quote }}
|
||||
{{- end }}
|
||||
{{- if and $appResources.limits $appResources.limits.cpu }}
|
||||
SHUFFLE_APP_CPU_LIMIT: {{ $appResources.limits.cpu | quote }}
|
||||
{{- end }}
|
||||
{{- if and $appResources.limits $appResources.limits.memory }}
|
||||
SHUFFLE_APP_MEMORY_LIMIT: {{ $appResources.limits.memory | quote }}
|
||||
{{- end }}
|
||||
{{- if and $appResources.limits (index $appResources.limits "ephemeral-storage") }}
|
||||
SHUFFLE_APP_EPHEMERAL_STORAGE_LIMIT: {{ (index $appResources.limits "ephemeral-storage") | quote }}
|
||||
{{- end }}
|
||||
@@ -82,16 +82,17 @@ spec:
|
||||
args: {{- include "common.tplvalues.render" (dict "value" .Values.orborus.args "context" $) | nindent 12 }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: RUNNING_MODE
|
||||
value: kubernetes
|
||||
- name: IS_KUBERNETES
|
||||
value: "true"
|
||||
- name: CLEANUP
|
||||
value: "false" # Do not remove resources when restarting orborus
|
||||
{{- $env := include "shuffle.orborus.env" . | fromYaml }}
|
||||
{{- range $key, $val := $env }}
|
||||
- name: {{ $key | quote }}
|
||||
value: {{ $val | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.orborus.extraEnvVars }}
|
||||
{{- include "common.tplvalues.render" (dict "value" .Values.orborus.extraEnvVars "context" $) | nindent 12 }}
|
||||
{{- end }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "shuffle.orborus.name" . }}-env
|
||||
{{- if .Values.orborus.extraEnvVarsCM }}
|
||||
- configMapRef:
|
||||
name: {{ include "common.tplvalues.render" (dict "value" .Values.orborus.extraEnvVarsCM "context" $) }}
|
||||
|
||||
@@ -9,6 +9,7 @@ metadata:
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- if .Values.orborus.manageWorkerDeployments }}
|
||||
- verbs:
|
||||
- list
|
||||
- create
|
||||
@@ -26,4 +27,19 @@ rules:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
{{- else }}
|
||||
- verbs:
|
||||
- list
|
||||
apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- pods
|
||||
- services
|
||||
- verbs:
|
||||
- list
|
||||
apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
{{/*
|
||||
Return the common name for app components.
|
||||
*/}}
|
||||
{{- define "shuffle.app.name" -}}
|
||||
{{- printf "%s-app" (include "common.names.fullname" .) | trunc 63 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the common labels for app components deployed via helm.
|
||||
NOTE: App deployments and services use shuffle.appInstance.labels instead.
|
||||
*/}}
|
||||
{{- define "shuffle.app.labels" -}}
|
||||
{{- include "common.labels.standard" . }}
|
||||
app.kubernetes.io/component: app
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use for Shuffle apps
|
||||
*/}}
|
||||
{{- define "shuffle.app.serviceAccount.name" -}}
|
||||
{{- if .Values.app.serviceAccount.create -}}
|
||||
{{ default (include "shuffle.app.name" .) .Values.app.serviceAccount.name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.app.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the proper Docker Image Registry Secret Names for the app service account
|
||||
*/}}
|
||||
{{- define "shuffle.app.serviceAccount.imagePullSecrets" -}}
|
||||
{{- $pullSecrets := list }}
|
||||
|
||||
{{- range .Values.global.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range .Values.app.serviceAccount.imagePullSecrets -}}
|
||||
{{- if kindIs "map" . -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" .)) -}}
|
||||
{{- else -}}
|
||||
{{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if (not (empty $pullSecrets)) -}}
|
||||
imagePullSecrets:
|
||||
{{- range $pullSecrets | uniq }}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels for ALL apps.
|
||||
These match the labels of helm-deployed apps (shuffle.appInstance.labels),
|
||||
as well as worker-deployed apps (deployK8sApp).
|
||||
*/}}
|
||||
{{- define "shuffle.app.matchLabels" -}}
|
||||
app.kubernetes.io/name: shuffle-app
|
||||
{{- end -}}
|
||||
|
||||
|
||||
{{/*
|
||||
Return the sanitized name of a shuffle app.
|
||||
Usage:
|
||||
{{ include "shuffle.appInstance.name" $app }}
|
||||
*/}}
|
||||
{{- define "shuffle.appInstance.name" -}}
|
||||
{{ .name | replace "_" "-" | lower }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the sanitized name of a shuffle app, including the version of the app.
|
||||
Usage:
|
||||
{{ include "shuffle.appInstance.fullname" $app }}
|
||||
*/}}
|
||||
{{- define "shuffle.appInstance.fullname" -}}
|
||||
{{ printf "%s-%s" .name .version | replace "." "-" | replace "_" "-" | lower }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the labels for a shuffle app deployed by helm.
|
||||
Usage:
|
||||
{{ include "shuffle.appInstance.labels" (dict "app" $app "customLabels" .Values.commonLabels "context" $) }}
|
||||
*/}}
|
||||
{{- define "shuffle.appInstance.labels" -}}
|
||||
{{- $customLabels := mustMerge (dict "app.kubernetes.io/name" "shuffle-app" "app.kubernetes.io/part-of" "shuffle") (include "common.tplvalues.render" (dict "value" .customLabels "context" .context) | fromYaml) -}}
|
||||
{{ include "common.labels.standard" (dict "customLabels" $customLabels "context" .context) }}
|
||||
app.shuffler.io/name: {{ include "shuffle.appInstance.name" .app }}
|
||||
app.shuffler.io/version: {{ .app.version | quote }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the match labels of a single app, deployed via helm.
|
||||
Usage:
|
||||
{{ include "shuffle.appInstance.matchLabels" (dict "app" $app "customLabels" .Values.commonLabels "context" $) }}
|
||||
*/}}
|
||||
{{- define "shuffle.appInstance.matchLabels" -}}
|
||||
{{- $customLabels := mustMerge (dict "app.kubernetes.io/name" "shuffle-app" "app.kubernetes.io/part-of" "shuffle") (include "common.tplvalues.render" (dict "value" .customLabels "context" .context) | fromYaml) -}}
|
||||
{{ include "common.labels.matchLabels" (dict "customLabels" $customLabels "context" .context) }}
|
||||
app.shuffler.io/name: {{ include "shuffle.appInstance.name" .app }}
|
||||
app.shuffler.io/version: {{ .app.version | quote }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return a podAffinity/podAntiAffinity definition.
|
||||
Usage:
|
||||
{{ include "shuffle.appInstance.affinities.pods" (dict "type" "soft" "app" $app "customLabels" $podLabels "context" $) -}}
|
||||
*/}}
|
||||
{{- define "shuffle.appInstance.affinities.pods" -}}
|
||||
{{- $customLabels := mustMerge (dict "app.kubernetes.io/name" "shuffle-app" "app.kubernetes.io/part-of" "shuffle") (include "common.tplvalues.render" (dict "value" .customLabels "context" .context) | fromYaml) -}}
|
||||
{{- $extraMatchLabels := dict "app.shuffler.io/name" (include "shuffle.appInstance.name" .app) "app.shuffler.io/version" (.app.version) }}
|
||||
{{ include "common.affinities.pods" (dict "type" .type "customLabels" $customLabels "context" .context "extraMatchLabels" $extraMatchLabels )}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the environment variables of shuffle apps in the format
|
||||
KEY: VALUE
|
||||
*/}}
|
||||
{{- define "shuffle.appInstance.env" -}}
|
||||
SHUFFLE_APP_SDK_TIMEOUT: {{ .Values.app.sdkTimeout | quote }}
|
||||
SHUFFLE_APP_EXPOSED_PORT: {{ .Values.app.exposedContainerPort | quote }}
|
||||
SHUFFLE_LOGS_DISABLED: {{ .Values.app.disableLogs | quote }}
|
||||
{{- end -}}
|
||||