feat: click to deploy to AWS test

This commit is contained in:
Aditya
2024-11-20 17:42:37 +05:30
80 changed files with 5537 additions and 7738 deletions
-13
View File
@@ -1,13 +0,0 @@
on:
push:
branches:
- launch
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
with:
release-type: node
package-name: release-please-action
-51
View File
@@ -1,51 +0,0 @@
# This is a basic workflow to help you get started with Actions
name: App SDK upload
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ master, launch ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v0'
with:
credentials_json: '${{ secrets.SANDBOX_CREDENTIALS }}'
- id: 'upload_sdk'
name: Cloud Storage Uploader
uses: google-github-actions/upload-cloud-storage@v0.9.0
with:
path: 'backend/app_sdk/app_base.py'
destination: 'shuffle-sandbox-337810.appspot.com/generated_apps/baseline'
- id: 'upload_requirement'
name: Cloud Storage Uploader
uses: google-github-actions/upload-cloud-storage@v0.9.0
with:
path: 'backend/app_sdk/requirements.txt'
destination: 'shuffle-sandbox-337810.appspot.com/generated_apps/baseline'
- id: 'upload_Dockerfile'
name: Cloud Storage Uploader
uses: google-github-actions/upload-cloud-storage@v0.9.0
with:
path: 'backend/app_sdk/Dockerfile'
destination: 'shuffle-sandbox-337810.appspot.com/generated_apps/baseline'
+3
View File
@@ -4,10 +4,13 @@
Shuffle Automation
<<<<<<< HEAD
[![CodeQL](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml)
[![Autobuild](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml)
[![Deploy to AWS](https://d1.awsstatic.com/cloudformation-deploy-to-aws-button.png)](https://console.aws.amazon.com/cloudformation/home?#/stacks/new?stackName=Shuffle-Instance&templateURL=https://raw.githubusercontent.com/Shuffle/Shuffle/refs/heads/2.0.0/template.yaml)
=======
>>>>>>> 8883de54d10855428866f598b70e16674df6d7c1
</h1><h4 align="center">
[Shuffle](https://shuffler.io) is an automation platform for and by the community, focusing on accessibility for anyone to automate. Security operations is complex, but it doesn't have to be.
-9
View File
@@ -1,9 +0,0 @@
{
"name": "Shuffle",
"description": "Security Automation Platform",
"repository": "https://github.com/0x0elliot/Shuffle",
"ref": "2.0.0",
"scripts": {
"postclone": "chmod +x startup.sh && ./startup.sh"
}
}
-21
View File
@@ -1,21 +0,0 @@
#FROM python:3.9.1-alpine as base
FROM python:3.10.0-alpine as base
#FROM python:3.11.3-alpine as base
FROM base as builder
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev tzdata coreutils
RUN mkdir /install
WORKDIR /install
FROM base
#--no-cache
RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev coreutils
COPY --from=builder /install /usr/local
COPY requirements.txt /requirements.txt
RUN pip3 install -r /requirements.txt
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-42
View File
@@ -1,42 +0,0 @@
FROM python:3.10.0-alpine as base
FROM base as builder
RUN apk --no-cache add --update \
alpine-sdk \
build-base \
g++ \
gcc \
libffi \
libffi-dev \
libstdc++ \
linux-headers \
musl-dev \
openssl-dev \
tzdata \
coreutils
RUN pip install --upgrade pip && \
pip install --prefix="/install" --no-cache-dir grpcio grpcio-tools && \
apk del --purge \
g++ \
gcc \
musl-dev \
libffi-dev \
libstdc++ \
build-base \
linux-headers
RUN mkdir -p /install
WORKDIR /install
FROM base
#--no-cache
RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev coreutils
COPY --from=builder /install /usr/local
COPY requirements.txt /requirements.txt
RUN pip3 install -r /requirements.txt
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-19
View File
@@ -1,19 +0,0 @@
FROM blackarchlinux/blackarch as base
FROM base as builder
RUN /bin/pacman -Syu --noconfirm
RUN /bin/pacman -Sy --noconfirm base-devel libffi musl openssl python python-pip -y
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN pip install --prefix="/install" -r /requirements.txt
FROM base
COPY --from=builder /install /usr/local
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-19
View File
@@ -1,19 +0,0 @@
FROM kalilinux/kali-rolling as base
FROM base as builder
RUN apt-get update
RUN apt-get dist-upgrade -y
RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN pip install --prefix="/install" -r /requirements.txt
FROM base
COPY --from=builder /install /usr/local
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-22
View File
@@ -1,22 +0,0 @@
FROM ubuntu as base
FROM base as builder
RUN apt-get update
RUN apt-get dist-upgrade -y
RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN pip install --prefix="/install" -r /requirements.txt
FROM base
RUN apt-get update
RUN apt-get dist-upgrade -y
RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y
COPY --from=builder /install /usr/local
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Frikkylikeme
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Executable → Regular
+2 -22
View File
@@ -1,22 +1,2 @@
# app_sdk.py
This is the SDK used for apps to behave like they should.
## If you want to update apps.. PS: downloads from docker hub do overrides.. :)
1. Write your code & check if runtime works
2. Build app_base image
3. docker rm $(docker ps -aq) # Remove all stopped containers
4. Delete the specific app's Docker image (docker rmi frikky/shuffle:...)
5. Rebuild the Docker image (click load in GUI?)
## Cloud updates
1. Go to shuffle cloud on GCP
2. Go to Cloud Storage
3. Find shuffler.appspot.com
4. Navigate to generated_apps/baseline
5. Update SDK there. This will make all new apps run with the new SDK
## Cloud app force-updates
1. Run the "stitcher.go" program in the public shuffle-shared repository.
# LICENSE
Everything in here is MIT, not AGPLv3 as indicated by the license.
## CHANGES
In November 2024, we moved this to its own repistory: https://github.com/shuffle/app_sdk
View File
File diff suppressed because it is too large Load Diff
-187
View File
@@ -1,187 +0,0 @@
import re
import json
input_data = """{
"test4": $test,
"test5": ,
"test6": "what"
}
"""
input_data = """{
"test0": {{ '' | default: [] }},
"test": {{ | default: [] }},
"test2": {{ $test.asd | default: [] }},
"test3": {{ {"key": "val} | default: [] }},
"test4": $test,
"test5": ,
"test6": "what"
}
"""
liquiddata = "{{ $test.asd | some other stuff {{ $test.xyz | more stuff"
pattern = r'\{\{\s*\$[^|}]+\s*\|'
replaced_data = re.sub(pattern, "{{ '' |", liquiddata)
print(replaced_data)
def patternfix_string(liquiddata, patterns, regex_patterns, inputtype="liquid"):
if not inputtype or inputtype == "liquid":
if "{{" not in liquiddata or "}}" not in liquiddata:
return liquiddata
elif inputtype == "json":
liquiddata = liquiddata.strip()
# Validating if it looks like json or not
if liquiddata[0] == "{" and liquiddata[len(liquiddata)-1] == "}":
pass
else:
if liquiddata[0] == "[" and liquiddata[len(liquiddata)-1] == "]":
pass
else:
return liquiddata
# If it's already json, don't touch it
try:
json.loads(liquiddata)
return liquiddata
except Exception as e:
pass
else:
print("No replace handler for %s" % inputtype)
return liquiddata
skipkeys = [" "]
newoutput = liquiddata[:]
for pattern in patterns:
keylocations = []
parsedvalue = ""
record = False
index = -1
for key in liquiddata:
# Return instant if possible
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
pass
index += 1
if not key:
if record:
keylocations.append(index)
parsedvalue += key
continue
if key in skipkeys:
if record:
keylocations.append(index)
parsedvalue += key
continue
if key == pattern[0] and not record:
record = True
if key not in pattern:
keylocations = []
parsedvalue = ""
record = False
if record:
keylocations.append(index)
parsedvalue += key
if len(parsedvalue) == 0:
continue
evaluated_value = parsedvalue[:]
for skipkey in skipkeys:
evaluated_value = "".join(evaluated_value.split(skipkey))
if evaluated_value == pattern:
#print("Found matching: %s (%s)" % (parsedvalue, keylocations))
#print("Should replace with: %s" % patterns[pattern])
newoutput = newoutput.replace(parsedvalue, patterns[pattern], -1)
# Return instant if possible
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
pass
for pattern in regex_patterns:
newlines = []
for line in newoutput.split("\n"):
replaced_line = re.sub(pattern, regex_patterns[pattern], line)
newlines.append(replaced_line)
newoutput = "\n".join(newlines)
# Return instant if possible
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
pass
# Dont return json properly unless actually json
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
# Returns original if json fixing didn't work
return liquiddata
return newoutput
print("Start:\n%s" % input_data)
try:
newinput = patternfix_string(input_data,
{
"{{|": '{{ "" |',
},
{
#r'\{\{\s*|': "{{ '' |",
r'\{\{\s*\$[^|}]+\s*\|': '{{ "" |',
}
,
inputtype="liquid"
)
except Exception as e:
print("[ERROR} Failed liquid parsing fix: %s" % e)
newinput = input_data
try:
newinput = patternfix_string(newinput,
{
},
{
r'\"\s*\:\s*,': '\": "",',
r'\"\s*\:\s*\$[^,]+\w*\,': '\": "",',
}
,
inputtype="json"
)
try:
json.loads(newinput)
print("It's json! Override.")
except Exception as e:
print("Bad json. DONT use the value at all: %s" % e)
except Exception as e:
print("[ERROR} Failed json parsing fix: %s" % e)
print("\nEnd:\n%s" % newinput)
-51
View File
@@ -1,51 +0,0 @@
#!/bin/bash
### DEFAULT
NAME=shuffle-app_sdk
VERSION=1.2.0
docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force
docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION -t ghcr.io/frikky/$NAME:nightly -t shuffle/shuffle:app_sdk -t shuffle/$NAME:$VERSION -t docker.pkg.github.com/shuffle/shuffle/$NAME:$VERSION -t ghcr.io/shuffle/$NAME:$VERSION -t ghcr.io/shuffle/$NAME:nightly
docker push frikky/shuffle:app_sdk
docker push ghcr.io/frikky/$NAME:$VERSION
docker push ghcr.io/frikky/$NAME:nightly
docker push ghcr.io/frikky/$NAME:latest
docker push shuffle/shuffle:app_sdk
docker push ghcr.io/shuffle/$NAME:$VERSION
docker push ghcr.io/shuffle/$NAME:nightly
docker push ghcr.io/shuffle/$NAME:latest
#### UBUNTU
NAME=shuffle-app_sdk_ubuntu
docker build . -f Dockerfile_ubuntu -t frikky/shuffle:app_sdk_ubuntu -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
docker push frikky/shuffle:app_sdk_ubuntu
docker push ghcr.io/frikky/$NAME:$VERSION
#### Alpine GRPC
NAME=shuffle-app_sdk_grpc
docker build . -f Dockerfile_alpine_grpc -t frikky/shuffle:app_sdk_grpc -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
docker push frikky/shuffle:app_sdk_grpc
docker push ghcr.io/frikky/$NAME:$VERSION
#### KALI ###
#NAME=shuffle-app_sdk_kali
#docker build . -f Dockerfile_kali -t frikky/shuffle:app_sdk_kali -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
#
#docker push frikky/shuffle:app_sdk_kali
#docker push ghcr.io/frikky/$NAME:$VERSION
#docker push ghcr.io/frikky/$NAME:nightly
### BLACKARCH ###
#NAME=shuffle-app_sdk_blackarch
#docker build . -f Dockerfile_blackarch -t frikky/shuffle:app_sdk_blackarch -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
#
#docker push frikky/shuffle:app_sdk_blackarch
#docker push ghcr.io/frikky/$NAME:$VERSION
#docker push ghcr.io/frikky/$NAME:nightly
-326
View File
@@ -1,326 +0,0 @@
## A test script of the recurse_json function
## to validate that it can handle the different types of data
## and follow the dot formation format
import re
import json
def recurse_json(basejson, parsersplit):
match = "#([0-9a-z]+):?-?([0-9a-z]+)?#?"
try:
outercnt = 0
# Loops over split values
splitcnt = -1
for value in parsersplit:
splitcnt += 1
#if " " in value:
# value = value.replace(" ", "_", -1)
actualitem = re.findall(match, value, re.MULTILINE)
# Goes here if loop
if value == "#":
newvalue = []
if basejson == None:
return "", False
for innervalue in basejson:
# 1. Check the next item (message)
# 2. Call this function again
try:
ret, is_loop = recurse_json(innervalue, parsersplit[outercnt+1:])
except IndexError:
# Only in here if it's the last loop without anything in it?
ret, is_loop = recurse_json(innervalue, parsersplit[outercnt:])
newvalue.append(ret)
# Magical way of returning which makes app sdk identify
# it as multi execution
return newvalue, True
# Checks specific regex like #1-2 for index 1-2 in a loop
elif len(actualitem) > 0:
is_loop = True
newvalue = []
firstitem = actualitem[0][0]
seconditem = actualitem[0][1]
if isinstance(firstitem, int):
firstitem = str(firstitem)
if isinstance(seconditem, int):
seconditem = str(seconditem)
#print("[DEBUG] ACTUAL PARSED: %s" % actualitem)
# Means it's a single item -> continue
if seconditem == "":
#print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson)))
if str(firstitem).lower() == "max" or str(firstitem).lower() == "last" or str(firstitem).lower() == "end":
firstitem = len(basejson)-1
elif str(firstitem).lower() == "min" or str(firstitem).lower() == "first":
firstitem = 0
else:
firstitem = int(firstitem)
#print(f"[DEBUG] Post lower checks with item {firstitem}")
tmpitem = basejson[int(firstitem)]
try:
newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:])
except IndexError:
newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:])
else:
#print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem))
if isinstance(firstitem, str):
if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end":
firstitem = len(basejson)-1
elif firstitem.lower() == "min" or firstitem.lower() == "first":
firstitem = 0
else:
firstitem = int(firstitem)
else:
firstitem = int(firstitem)
if isinstance(seconditem, str):
if str(seconditem).lower() == "max" or str(seconditem).lower() == "last" or str(firstitem).lower() == "end":
seconditem = len(basejson)-1
elif str(seconditem).lower() == "min" or str(seconditem).lower() == "first":
seconditem = 0
else:
seconditem = int(seconditem)
else:
seconditem = int(seconditem)
#print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}")
newvalue = []
if int(seconditem) > len(basejson):
seconditem = len(basejson)
for i in range(int(firstitem), int(seconditem)+1):
# 1. Check the next item (message)
# 2. Call this function again
try:
ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:])
except IndexError:
#print("[DEBUG] INDEXERROR (1): ", parsersplit[outercnt])
#ret = innervalue
ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt:])
newvalue.append(ret)
return newvalue, is_loop
else:
if len(value) == 0:
return basejson, False
try:
if isinstance(basejson, list):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
try:
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
# Should we sanitize here?
#print("[DEBUG] VALUE TO SANITIZE FOR KEY '%s'?: %s" % (value, basejson[value]))
# Check if we are on the last item?
if outercnt == len(parsersplit)-1:
#print("[DEBUG] LAST KEY")
return str(basejson[value]), False
else:
#print("[DEBUG] NOT LAST KEY")
pass
except json.decoder.JSONDecodeError as e:
return str(basejson[value]), False
else:
basejson = basejson[value]
except KeyError as e:
print("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e))
if "_" in value:
value = value.replace("_", " ", -1)
elif " " in value:
value = value.replace(" ", "_", -1)
try:
if isinstance(basejson, list):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
#print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value])
try:
#print("[DEBUG] BASEJSON: %s" % basejson)
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
if outercnt == len(parsersplit)-1:
#print("LAST KEY (2)")
return str(basejson[value]), False
else:
#print("NOT LAST KEY (2)")
pass
except json.decoder.JSONDecodeError as e:
#print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value])
return str(basejson[value]), False
else:
basejson = basejson[value]
except KeyError as e:
# Check if previous key was handled or not
previouskey = parsersplit[outercnt-1]
#print("[DEBUG] PREVIOUS KEY: ", previouskey)
tmpval = previouskey + "." + value
#print("\n\n[WARNING] Running third dot notation fix '%s' on data %s: %s" % (value, basejson, e))
if tmpval in basejson:
return basejson[tmpval], False
try:
currentsplitcnt = splitcnt
recursed_value = value
handled = False
#tmpbase = basejson
previouskey = value
while True:
#print("\n\n[DEBUG] CURRENTSPLITCNT: ", currentsplitcnt)
newvalue = parsersplit[currentsplitcnt+1]
if newvalue == "#" or newvalue == "":
break
recursed_value += "." + newvalue
#print("\n\nRECURSED: ", recursed_value)
found = False
for key, value in basejson.items():
if recursed_value.lower() in key.lower():
found = True
if found == False:
#print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value)
# Check if we are on the last key or not
return "", False
#if outercnt == len(parsersplit)-1:
# print("[DEBUG] LAST KEY (3)")
# break
#else:
# print("[DEBUG] NOT LAST KEY (3)")
# return "", False
if recursed_value in basejson:
#print("[INFO] FOUND RECURSED VALUE: ", recursed_value)
basejson = basejson[recursed_value]
# Whether to dig deeper or not
if isinstance(basejson, bool) or isinstance(basejson, int) or isinstance(basejson, str):
handled = False
else:
handled = True
break
currentsplitcnt += 1
if handled:
continue
break
except IndexError as e:
print("[DEBUG] INDEXERROR (2):", parsersplit[outercnt])
return "", False
outercnt += 1
except KeyError as e:
print("[INFO] Lower keyerror: %s" % e)
return "", False
except Exception as e:
print("[WARNING] Exception: %s" % e)
return "", False
return basejson, False
print("[INFO] Starting")
#input_data = "test"
#input_data = "test2.data"
# Matchwith
basejson = {
"test": "hello",
"test2": {
"test3": "hello2",
"test3.data": "hello3",
"test4.data.testing": {
"value": "hello4"
},
"test5.data.hello": "wut",
},
"test3": ["hello", "hello2", "hello3"],
"test4": [{
"id": "1",
}]
}
# Inputexamples (ALL should be True)
inputs = {
#"": "",
"badkey": "",
"test": "hello",
"test2.badkey": "",
"test2.test3": "hello2",
"test2.test3.data": "hello3",
"test2.test4.data.testing": "{'value': 'hello4'}", # FIXME: Doesn't work due to break vs return "", False in last exception
"test2.test4.data.testing.value": "hello4", # FIXME: Doesn't work due to break vs return "", False in last exception. Not fixed as we didn't find one of these yet.
"test2.test5.data.hello": "wut",
"test2.test5.data.badkey": "",
"test3.#1": "hello2",
"test4.#0.id": "1",
"test4.#1.id": "",
}
outputs = []
for key, value in inputs.items():
parsersplit = key.split(".")
ret, is_loop = recurse_json(basejson, parsersplit)
print("\n\nOUTPUT RET (%s): %s" % (key, ret))
outputs.append("[%s]: %s = '%s' vs '%s'" % (str(ret) == str(value), key, ret, value))
print("\n\n%s" % "\n".join(outputs))
#input_data = ""
#input_data = "badkey"
#input_data = "test"
#input_data = "test2.data"
#input_data = "test2.test3.data"
#input_data = "test2.test4.data.testing.value.as"
#input_data = "test2.test5.data.hello"
-9
View File
@@ -1,9 +0,0 @@
urllib3==1.26.18
requests==2.31.0
MarkupSafe==2.0.1
liquidpy==0.8.1
flask[async]==2.0.2
waitress==2.1.0
#flask==1.1.2
python-dateutil==2.8.1
PyJWT==2.9.0
+2 -19
View File
@@ -35,8 +35,8 @@ import (
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/storage/memory"
gitProxy "github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/storage/memory"
// Random
xj "github.com/basgys/goxml2json"
@@ -257,7 +257,6 @@ type Hook struct {
Environment string `json:"environment" datastore:"environment"`
}
func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"id": "12345",
@@ -559,7 +558,6 @@ func createNewUser(username, password, role, apikey string, org shuffle.OrgMini)
}
}
return nil
}
@@ -932,7 +930,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
log.Printf("[DEBUG] Failed to get org during getinfo: %s", err)
}
//if err == nil {
if len(org.Id) > 0 {
if userInfo.Role == "" {
@@ -1092,7 +1089,6 @@ type passwordReset struct {
Reference string `json:"reference"`
}
func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request)
if cors {
@@ -3338,7 +3334,6 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s
}
}
log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID)
if len(user.Id) > 0 {
resp.WriteHeader(200)
@@ -3379,8 +3374,6 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
buildSwaggerApp(resp, body, user, false)
}
// Hotloads new apps from a folder
func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error {
@@ -3727,7 +3720,6 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error {
return nil
}
func remoteOrgJobHandler(org shuffle.Org, interval int) error {
// Check if it's 1 in 10 (10% chance random)
@@ -3777,7 +3769,6 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
backupJobData = []byte{}
}
syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl)
client := shuffle.GetExternalClient(syncUrl)
req, err := http.NewRequest(
@@ -4340,7 +4331,6 @@ func runInitEs(ctx context.Context) {
log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps))
}
if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" {
healthcheckInterval := 30
log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval)
@@ -4369,7 +4359,6 @@ func runInitEs(ctx context.Context) {
log.Printf("[INFO] Finished INIT (ES)")
}
func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) {
ctx := context.Background()
org, err := shuffle.GetOrg(ctx, orgId)
@@ -4948,8 +4937,6 @@ func makeWorkflowPublic(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
}
func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request)
if cors {
@@ -5008,8 +4995,6 @@ func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte("OK"))
}
func initHandlers() {
var err error
ctx := context.Background()
@@ -5056,7 +5041,7 @@ func initHandlers() {
r.HandleFunc("/api/v1/users/register", handleRegister).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/{userId}/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS")
@@ -5244,7 +5229,6 @@ func initHandlers() {
r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/orgs/{orgId}/datastore/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
// Docker orborus specific - downloads an image
r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS")
@@ -5274,7 +5258,6 @@ func initHandlers() {
r.HandleFunc("/api/v1/detections/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS")
//r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS")
// Introduced in 0.9.21 to handle notifications for e.g. failed Workflow
r.HandleFunc("/api/v1/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS")
+2 -1
View File
@@ -21,6 +21,7 @@
"@uiw/codemirror-themes": "^4.21.9",
"@uiw/react-codemirror": "^4.21.21",
"algoliasearch": "^4.8.3",
"chart.js": "^3.0.0",
"class-transformer": "^0.2.0",
"codemirror": "^6.0.1",
"cpx": "^1.5.0",
@@ -60,7 +61,7 @@
"react-alice-carousel": "^2.6.4",
"react-avatar-editor": "^11.1.0",
"react-beforeunload": "^2.2.1",
"react-chartjs-2": "^5.2.0",
"react-chartjs-2": "^5.0.0",
"react-cookie": "^4.0.1",
"react-cytoscapejs": "^2.0.0",
"react-device-detect": "^2.2.3",
+19 -1
View File
@@ -14,6 +14,7 @@ import HealthPage from "./components/HealthPage.jsx";
//import Header from "./components/Header.jsx";
import theme from "./theme";
import Apps from "./views/Apps";
import Apps2 from "./views/Apps2.jsx";
import AppCreator from "./views/AppCreator";
import DetectionDashBoard from "./views/DetectionDashboard.jsx";
@@ -48,7 +49,7 @@ import 'react-toastify/dist/ReactToastify.css';
import Drift from "react-driftjs";
import { AppContext } from './context/contextApi.jsx';
import { AppContext } from './context/ContextApi.jsx';
// Production - backend proxy forwarding in nginx
var globalUrl = window.location.origin;
@@ -408,6 +409,23 @@ const App = (message, props) => {
{...props}
/>
}
/>
<Route
exact
path="/apps2"
element={
<Apps2
serverside={false}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
checkLogin={checkLogin}
userdata={userdata}
globalUrl={globalUrl}
surfaceColor={theme.palette.surfaceColor}
inputColor={theme.palette.inputColor}
{...props}
/>
}
/>
<Route
exact
+3 -3
View File
@@ -1972,14 +1972,14 @@ const AppFramework = (props) => {
{data.name}
</Typography>
<div style={{display: "flex", width: 200, margin: "auto", marginTop: 15, }}>
<div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginRight: 15, position: "relative",}}>
<div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginRight: 15, position: "relative",}}>
{parsedLeftImage}
{parsedLeftText}
</div>
<div style={{backgroundColor: theme.palette.inputColor, maxHeight: 30, maxWidth: 30, height: 30, width: 30, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginTop: 22, padding: "10px 0px 0px 9px",}}>
<div style={{backgroundColor: theme.palette.inputColor, maxHeight: 30, maxWidth: 30, height: 30, width: 30, borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginTop: 22, padding: "10px 0px 0px 9px",}}>
{svgIcon}
</div>
<div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginLeft: 15, position: "relative",}}>
<div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginLeft: 15, position: "relative",}}>
{parsedRightImage}
{parsedRightText}
</div>
+10 -1
View File
@@ -464,8 +464,17 @@ const AppGrid = (props) => {
}}
>
<img
id={`image_${index}`}
alt={data.name}
src={data.image_url ? data.image_url : "/images/no_image.png"}
onError={(e) => {
// Replace the image with the default image
const foundImage = document.getElementById(`image_${index}`)
if (foundImage !== undefined && foundImage !== null) {
foundImage.src = theme.palette.defaultImage
data.image_url = theme.palette.defaultImage
}
}}
style={{
width: 80,
height: 80,
@@ -995,7 +1004,7 @@ const AppGrid = (props) => {
}}
autoComplete="off"
color="primary"
placeholder="Search your Activated or Self-built apps"
placeholder="Search your Activated or self-built apps"
id="shuffle_search_field"
onChange={(event) => {
setSearchQuery(event.currentTarget.value);
+7 -1
View File
@@ -47,6 +47,7 @@ const AppSelection = props => {
defaultSearch,
setDefaultSearch,
checkLogin,
isAppPage=false
} = props;
const [discoveryData, setDiscoveryData] = React.useState({})
@@ -507,11 +508,13 @@ const AppSelection = props => {
})}
</Grid>
</div>
{
!isAppPage && (
<>
{!moreButton ? (
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}>
<Link style={{ color: "#FF8444" }} onClick={() => {
setMoreButton(true)
setTimeout(() => {
navigate("/welcome?tab=2")
}, 250)
@@ -527,6 +530,9 @@ const AppSelection = props => {
See usecases
</Button>
</div>
</>
)
}
</div>
</Fade>
)
-3
View File
@@ -27,9 +27,6 @@ const Appsearch = props => {
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs
//const theme = useTheme();
//const [apps, setApps] = React.useState([]);
//const [filteredApps, setFilteredApps] = React.useState([]);
const [formMail, setFormMail] = React.useState("");
const [message, setMessage] = React.useState("");
const [formMessage, setFormMessage] = React.useState("");
@@ -45,21 +45,21 @@ const AuthenticationItem = (props) => {
data.fields = [
{
key: "url",
value: "Secret. Replaced during app execution!",
value: "URL Secret. Replaced during runtime",
},
{
key: "client_id",
value: "Secret. Replaced during app execution!",
value: "ClientID Secret. Replaced during runtime.",
},
{
key: "client_secret",
value: "Secret. Replaced during app execution!",
value: "Client Secret. Replaced during runtime.",
},
{
key: "scope",
value: "Secret. Replaced during app execution!",
value: "Scope Secret. Replaced during runtime.",
},
];
]
}
const deleteAuthentication = (data) => {
@@ -152,7 +152,7 @@ const AuthenticationItem = (props) => {
src={data.app.large_image}
style={{
maxWidth: 50,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
/>
style={{ minWidth: 75, maxWidth: 75 }}
@@ -222,7 +222,7 @@ const AuthenticationData = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -304,7 +304,7 @@ const AuthenticationData = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -294,7 +294,7 @@ const AuthenticationData = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -328,10 +328,10 @@ const AuthenticationData = (props) => {
const authenticationButtons = <span>
<Button
style={{ borderRadius: theme.palette.borderRadius, marginTop: authFieldsOnly ? 20 : 0 }}
style={{ borderRadius: theme.palette?.borderRadius, marginTop: authFieldsOnly ? 20 : 0 }}
onClick={() => {
setAuthenticationOptions(authenticationOption);
handleSubmitCheck();
setAuthenticationOptions(authenticationOption)
handleSubmitCheck()
}}
variant={"contained"}
disabled={submitSuccessful}
@@ -437,7 +437,7 @@ const AuthenticationData = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
+5 -5
View File
@@ -179,7 +179,7 @@ const Billing = (props) => {
height: 480,
// width: "100%",
backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius * 2,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
marginTop: 15,
@@ -583,7 +583,7 @@ const Billing = (props) => {
margin: "auto",
width: 100,
backgroundColor: "white",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
/>
: null}
@@ -653,7 +653,7 @@ const Billing = (props) => {
value={feature.split("Worker License: ")[1]}
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
id={fieldId}
onClick={() => { }}
@@ -1041,7 +1041,7 @@ const Billing = (props) => {
width: 340,
height: 480,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor,
borderRadius: theme.palette.borderRadius * 2,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
marginTop: 15,
@@ -1315,7 +1315,7 @@ const Billing = (props) => {
// maxWidth: 400,
width: 340,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor,
borderRadius: theme.palette.borderRadius * 2,
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
marginTop: 15,
+1 -1
View File
@@ -43,7 +43,7 @@ const LineChartWrapper = ({keys, inputname, height, width}) => {
const inputdata = keys.data === undefined ? keys : keys.data
return (
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}>
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}>
<Typography variant="h6" style={{marginBotton: 15, }}>
{inputname}
</Typography>
+11 -12
View File
@@ -147,12 +147,12 @@ const ConfigureWorkflow = (props) => {
}
if (apps === undefined || apps === null) {
console.log("Apps is undefined or null: ", apps)
//console.log("Apps is undefined or null: ", apps)
return null
}
if (appAuthentication === undefined || appAuthentication === null) {
console.log("App authentication is undefined or null: ", appAuthentication)
//console.log("App authentication is undefined or null: ", appAuthentication)
return null
}
@@ -427,7 +427,6 @@ const ConfigureWorkflow = (props) => {
trigger.index = key;
if (trigger.trigger_type === "WEBHOOK") {
console.log("Found webhook: ", trigger)
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
const findapp = trigger.app_association.name.toLowerCase()
@@ -599,7 +598,7 @@ const ConfigureWorkflow = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
endAdornment: <InputAdornment position="end"></InputAdornment>,
@@ -800,7 +799,7 @@ const ConfigureWorkflow = (props) => {
>
<div
style={{
border: filled ? `1px solid ${theme.palette.green}` : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, width: "100%", padding: 12, cursor: "pointer",
border: filled ? `1px solid ${theme.palette.green}` : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: "100%", padding: 12, cursor: "pointer",
}}
id="app-config"
>
@@ -997,7 +996,7 @@ const ConfigureWorkflow = (props) => {
justifyContent: "flex-start",
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
color: action.auth_done ? "#686a6c" : "#ffffff",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
minWidth: 350,
maxHeight: 50,
overflow: "hidden",
@@ -1037,7 +1036,7 @@ const ConfigureWorkflow = (props) => {
>
<img
alt={action.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={action.large_image}
/>
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
@@ -1057,7 +1056,7 @@ const ConfigureWorkflow = (props) => {
justifyContent: "flex-start",
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
color: action.auth_done ? "#686a6c" : "#ffffff",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
minWidth: 350,
maxHeight: 50,
overflow: "hidden",
@@ -1088,7 +1087,7 @@ const ConfigureWorkflow = (props) => {
>
<img
alt={action.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={action.large_image}
/>
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
@@ -1108,7 +1107,7 @@ const ConfigureWorkflow = (props) => {
justifyContent: "flex-start",
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
color: action.auth_done ? "#686a6c" : "#ffffff",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
minWidth: 350,
maxHeight: 50,
overflow: "hidden",
@@ -1123,7 +1122,7 @@ const ConfigureWorkflow = (props) => {
>
<img
alt={action.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={action.large_image}
/>
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
@@ -1275,7 +1274,7 @@ const ConfigureWorkflow = (props) => {
const [finishCount, setFinishCount] = useState(0)
return (
<div style={{backgroundColor: hovered ? theme.palette.inputColor : "inherit", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, cursor: "pointer", }}
<div style={{backgroundColor: hovered ? theme.palette.inputColor : "inherit", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, cursor: "pointer", }}
>
<div style={{display: "flex", marginLeft: 15, marginTop: 15, marginBottom: 15, }}
onClick={() => {
+1 -1
View File
@@ -100,7 +100,7 @@ const Detection = (props) => {
width: "100%",
padding: 50,
backgroundColor: theme.palette.backgroundColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
<Box
+60 -4
View File
@@ -10,10 +10,12 @@ import {
Paper,
Divider,
IconButton,
Tooltip,
} from "@mui/material";
import {
OpenInNew as OpenInNewIcon,
FmdGood as FmdGoodIcon,
} from "@mui/icons-material"
import { toast } from "react-toastify";
@@ -68,6 +70,7 @@ const DetectionExplorer = (props) => {
const [detectionWorkflowId, setDetectionWorkflowId] = useState("")
const [isDetectionValid, setIsDetectionValid] = useState(false)
const [availableDetection, setAvailableDetection] = React.useState([]);
const [environmentList, setEnvironmentList] = React.useState([])
const loadUsecases = () => {
const url = `${globalUrl}/api/v1/workflows/usecases`
@@ -146,7 +149,7 @@ const DetectionExplorer = (props) => {
}
setLoading(true);
const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect`;
const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect`
fetch(url, {
method: "GET",
@@ -177,7 +180,11 @@ const DetectionExplorer = (props) => {
if (responseJson.reason !== undefined && responseJson.reason !== null) {
toast(responseJson.reason)
} else {
toast(`Failed to connect to ${detectionInfo?.category}`);
if (responseJson.workflow_id === "" && responseJson.workflow_valid === false) {
toast.info(`Sent job to generate a Detection Workflow and enable ${detectionInfo?.category}. Please wait a minute and reload this UI.`);
} else {
toast.error(`Failed to connect to ${detectionInfo?.category}`);
}
}
if (responseJson.action !== undefined && responseJson.actio !== null && responseJson.action.length > 0) {
@@ -193,16 +200,52 @@ const DetectionExplorer = (props) => {
.catch((error) => {
setLoading(false);
console.log(`Error in connecting to ${detectionInfo?.category}: `, error);
toast(`An error occurred while connecting to ${detectionInfo?.category}`);
toast.error(`An error occurred while connecting to ${detectionInfo?.category}`);
});
}
const loadEnvironments = () => {
const url = `${globalUrl}/api/v1/getenvironments`
fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
return response.json()
})
.then((responseJson) => {
if (responseJson.success === false) {
return
}
if (responseJson.length == 0) {
return
}
setEnvironmentList(responseJson)
})
.catch((error) => {
console.log(`Error in loading environments: `, error);
})
}
useEffect(() => {
loadUsecases()
loadEnvironments()
}, [])
useEffect(() => {
if (detectionInfo === undefined || detectionInfo === null) {
return
}
if (detectionInfo.category === undefined || detectionInfo.category === null || detectionInfo.category === "") {
return
}
handleConnectClick()
}, [detectionInfo])
@@ -211,6 +254,8 @@ const DetectionExplorer = (props) => {
rule.description.toLowerCase().includes(searchQuery.toLowerCase())
)
const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.data_lake?.enabled === true).length : 0
return (
<Container>
<Paper
@@ -219,7 +264,7 @@ const DetectionExplorer = (props) => {
width: "100%",
padding: 50,
backgroundColor: theme.palette.backgroundColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
<Box
@@ -234,6 +279,7 @@ const DetectionExplorer = (props) => {
{detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rules)`}
</Typography>
<div style={{display: "flex", }}>
{workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
<div style={{display: "flex", }}>
<div style={{minWidth: 400, maxWidth: 400, }}>
@@ -282,6 +328,16 @@ const DetectionExplorer = (props) => {
isDetectionValid ? `Connected to ${detectionInfo?.category}` : `Fix ${detectionInfo?.category} connection`}
</Button>
}
{detectionInfo?.category === "SIGMA" || detectionInfo?.category === "SIEM" ?
<Tooltip title={`You have ${lakeNodes} available Data Lake node(s)`}>
<a href="/admin?tab=environments" style={{textDecoration: "none", color: "inherit", }} target="_blank" rel="noreferrer">
<FmdGoodIcon style={{marginLeft: 15, marginTop: 5, color: lakeNodes > 0 ? green : red}} />
</a>
</Tooltip>
: null}
</div>
</Box>
{filteredRules?.length > 0 ?
<Box
@@ -98,7 +98,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
return (
<Card style={{
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
minHeight: 100,
marginBottom: 10,
paddingBottom: 0,
@@ -145,7 +145,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
backgroundColor: theme.palette.inputColor,
color: "white",
height: 40,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
<MenuItem
@@ -197,7 +197,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
overflow: 'visible',
zIndex: 10,
//border: "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginTop: 5,
minHeight: 40,
+41 -7
View File
@@ -43,6 +43,7 @@ import {
RadioGroup,
FormControl,
FormLabel,
Slider,
} from "@mui/material";
@@ -65,7 +66,7 @@ import {
} from "@mui/icons-material";
const EditWorkflow = (props) => {
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, scrollTo, setRealtimeMarkdown, } = props
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, scrollTo, setRealtimeMarkdown, boxWidth, setBoxWidth, } = props
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
@@ -82,12 +83,19 @@ const EditWorkflow = (props) => {
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
const [inputMarkdown, setInputMarkdown] = React.useState(workflow.input_markdown !== undefined && workflow.input_markdown !== null ? workflow.input_markdown : "")
const [inputMarkdown, setInputMarkdown] = React.useState(workflow?.form_control?.input_markdown !== undefined && workflow?.form_control?.input_markdown !== null ? workflow?.form_control?.input_markdown : "")
const [scrollDone, setScrollDone] = React.useState(false)
const [selectedYieldActions, setSelectedYieldActions] = React.useState(workflow.output_yields !== undefined && workflow.output_yields !== null ? JSON.parse(JSON.stringify(workflow.output_yields)) : [])
const [selectedYieldActions, setSelectedYieldActions] = React.useState(workflow?.form_control?.output_yields !== undefined && workflow?.form_control?.output_yields !== null ? JSON.parse(JSON.stringify(workflow?.form_control?.output_yields)) : [])
const [formWidth, setFormWidth] = React.useState(boxWidth === undefined || boxWidth === null ? 500 : boxWidth)
const classes = useStyles();
useEffect(() => {
if (setBoxWidth !== undefined && boxWidth !== formWidth) {
setBoxWidth(formWidth)
}
}, [formWidth])
if (scrollTo !== undefined && scrollTo !== null && scrollTo.length > 0 && scrollDone === false) {
setTimeout(() => {
const foundScroll = document.getElementById(scrollTo)
@@ -308,9 +316,14 @@ const EditWorkflow = (props) => {
}
innerWorkflow.input_questions = validfields
innerWorkflow.input_markdown = inputMarkdown
innerWorkflow.output_yields = selectedYieldActions
if (innerWorkflow.form_control === undefined || innerWorkflow.form_control === null) {
innerWorkflow.form_control = {}
}
innerWorkflow.form_control.input_markdown = inputMarkdown
innerWorkflow.form_control.output_yields = selectedYieldActions
innerWorkflow.form_control.form_width = formWidth
innerWorkflow.name = name
innerWorkflow.description = description
@@ -1049,13 +1062,34 @@ const EditWorkflow = (props) => {
}
setInputMarkdown(e.target.value)
workflow.input_markdown = e.target.value
workflow.form_control.input_markdown = e.target.value
setWorkflow(workflow)
setUpdate(Math.random())
}}
/>
</div>
<div id="form_size">
<Typography variant="h6" style={{marginTop: 50, }}>
Form Size
</Typography>
<Typography variant="body2" color="textSecondary" style={{marginBottom: 20, }}>
Control the width of the form. It will grow vertically as needed.
</Typography>
<Slider
defaultValue={formWidth}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={10}
marks
min={300}
max={1000}
onChange={(e, value) => {
setFormWidth(value)
}}
/>
</div>
<div id="output_control">
<Typography variant="h6" style={{marginTop: 50, }}>
Output Control ({selectedYieldActions.length === 0 ? "No Returns" : selectedYieldActions.length === 1 ? "Returning 1 node" : `Returning ${selectedYieldActions.length} nodes`})
@@ -1117,7 +1151,7 @@ const EditWorkflow = (props) => {
<Tooltip color="primary" title={"Add more details"} placement="top">
<Button
style={{ margin: "auto", marginTop: 50, textAlign: "center", textTransform: "none", }}
style={{ margin: "auto", marginTop: 50, marginBottom: 100, textAlign: "center", textTransform: "none", }}
variant="outlined"
disabled={newWorkflow === true}
color="secondary"
+2 -2
View File
@@ -227,7 +227,7 @@ const ExploreWorkflow = (props) => {
<ArrowBackIosNewIcon />
</IconButton>
</Tooltip>
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette?.borderRadius, }}>
<AliceCarousel
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
items={formattedCarousel}
@@ -320,7 +320,7 @@ const ExploreWorkflow = (props) => {
<div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}>
<div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette.borderRadius, }}>
<div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette?.borderRadius, }}>
<Grid item xs={11} style={{}}>
{suggestedUsecases.length === 0 && usecasesSet ?
<Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)">
@@ -325,7 +325,7 @@ const FixWorkflowValidationErrors = (props) => {
width: 25,
height: 25,
marginRight: 10,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
border: `1px solid ${theme.palette.borderColor}`,
}}
/>
@@ -467,7 +467,7 @@ const FixWorkflowValidationErrors = (props) => {
backgroundColor: theme.palette.inputColor,
color: "white",
height: 40,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
<MenuItem
@@ -559,7 +559,7 @@ const FixWorkflowValidationErrors = (props) => {
justifyContent: !validating ? "flex-start" : "center",
textTransform: "none",
fontSize: 18,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
onClick={() => {
toast("Validating app")
+3 -5
View File
@@ -50,8 +50,6 @@ const hoverOutColor = "#e8eaf6"
const Header = props => {
const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props;
//const theme = useTheme();
//const alert = useAlert()
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
@@ -829,7 +827,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
},
}}
style={{
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.surfaceColor,
marginRight: 15,
color: "white",
@@ -904,7 +902,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
<Tooltip color="primary" title={parsedTitle} placement="left">
<div style={{display: "flex"}}>
{isCloud?<Typography variant="body2" style={{borderRadius: theme.palette.borderRadius, float: "left", margin: "0 0 0 0", marginRight: 25, }}>{regiontag}</Typography>:null} {image} <span style={{marginLeft: 8}}>{data.name}</span>
{isCloud?<Typography variant="body2" style={{borderRadius: theme.palette?.borderRadius, float: "left", margin: "0 0 0 0", marginRight: 25, }}>{regiontag}</Typography>:null} {image} <span style={{marginLeft: 8}}>{data.name}</span>
</div>
</Tooltip>
</MenuItem>
@@ -935,7 +933,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
null
:
<Tooltip title={`Amount of executions left: ${userdata.app_execution_usage} / ${userdata.app_execution_limit}. When the limit is reached, you can still use Shuffle normally, but your Workflow triggers may stop working. Reach out to support@shuffler.io to extend this limit.`}>
<div style={{maxHeight: 30, minHeight: 30, padding: 8, textAlign: "center", cursor: "pointer", borderRadius: theme.palette.borderRadius, marginRight: 10, marginTop: 12, backgroundColor: theme.palette.surfaceColor, minWidth: 60, maxWidth: 60, border: userdata.app_execution_usage/userdata.app_execution_limit >= 0.9 ? "#f86a3e" : null, }} onClick={() => {
<div style={{maxHeight: 30, minHeight: 30, padding: 8, textAlign: "center", cursor: "pointer", borderRadius: theme.palette?.borderRadius, marginRight: 10, marginTop: 12, backgroundColor: theme.palette.surfaceColor, minWidth: 60, maxWidth: 60, border: userdata.app_execution_usage/userdata.app_execution_limit >= 0.9 ? "#f86a3e" : null, }} onClick={() => {
console.log(userdata.appe_execution_usage/userdata.app_execution_limit)
if (window.drift !== undefined) {
window.drift.api.startInteraction({ interactionId: 326905 })
@@ -1,5 +1,8 @@
import React from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
const HealthBarChart = (props) => {
const { globalUrl, filteredData, options, onBarClick } = props;
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -87,7 +87,7 @@ const LicencePopup = (props) => {
const paperStyle = {
padding: 20,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
height: "100%",
}
@@ -193,7 +193,7 @@ const LicencePopup = (props) => {
return (
<Tooltip
style={{ borderRadius: theme.palette.borderRadius, }}
style={{ borderRadius: theme.palette?.borderRadius, }}
placement="bottom"
>
<div style={{}}>
@@ -319,7 +319,7 @@ const LicencePopup = (props) => {
margin: "auto",
width: 100,
backgroundColor: "white",
// borderRadius: theme.palette.borderRadius,
// borderRadius: theme.palette?.borderRadius,
}}
/>
: null}
@@ -389,7 +389,7 @@ const LicencePopup = (props) => {
value={feature.split("Worker License: ")[1]}
style={{
// backgroundColor: theme.palette.inputColor,
// borderRadius: theme.palette.borderRadius,
// borderRadius: theme.palette?.borderRadius,
}}
id={fieldId}
onClick={() => { }}
@@ -823,7 +823,7 @@ const LicencePopup = (props) => {
{errorMessage.length > 0 ? <Typography variant="h4">Error: {errorMessage}</Typography> : null}
<Card style={{
padding: 20,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
border: "1px solid #f85a3e",
}}>
<div>
+4 -6
View File
@@ -65,7 +65,6 @@ const useStyles = makeStyles((theme) => ({
},
},
dropdownMenu: {
marginTop: theme.spacing(1),
borderRadius: "12px !important",
zIndex: 10,
"& .MuiPaper-root": {
@@ -88,7 +87,6 @@ const useStyles = makeStyles((theme) => ({
},
},
dropdownMenuItem: {
padding: theme.spacing(2, 3),
fontSize: "16px",
fontWeight: 400,
color: "#fff",
@@ -503,7 +501,7 @@ const Header = (props) => {
minHeight: 370,
padding: 20,
backgroundColor: "rgba(0, 0, 0, 1)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
},
}}
>
@@ -928,7 +926,7 @@ const Header = (props) => {
},
}}
style={{
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.surfaceColor,
marginRight: 15,
color: "white",
@@ -1056,7 +1054,7 @@ const Header = (props) => {
<Typography
variant="body2"
style={{
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
float: "left",
margin: "0 0 0 0",
marginRight: 25,
@@ -1165,7 +1163,7 @@ const Header = (props) => {
padding: 8,
textAlign: "center",
cursor: "pointer",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginTop: 5,
backgroundColor: theme.palette.surfaceColor,
minWidth: 60,
+1 -2
View File
@@ -1,7 +1,7 @@
import React, {useState} from 'react';
import { useTheme } from '@mui/styles';
import {isMobile} from "react-device-detect";
import ReactGA from 'react-ga4';
import theme from '../theme.jsx';
import {
TextField,
@@ -12,7 +12,6 @@ import {
const Newsletter = (props) => {
const { globalUrl, } = props;
const theme = useTheme();
const [email, setEmail] = useState("");
const [msg, setMsg] = useState("");
const [buttonActive, setButtonActive] = useState(true);
+18 -16
View File
@@ -96,7 +96,7 @@ const AuthenticationOauth2 = (props) => {
autoAuth,
authButtonOnly,
isLoggedIn,
org_id,
setFinalized,
} = props;
@@ -148,7 +148,6 @@ const AuthenticationOauth2 = (props) => {
navigate(`/login?view=${window.location.pathname}&message=Log in to authenticate this app`)
}
console.log("Should automatically click the auto-auth button?: ", autoAuth)
if (autoAuth === true && selectedApp !== undefined) {
startOauth2Request()
}
@@ -452,6 +451,8 @@ const AuthenticationOauth2 = (props) => {
if (orgId !== undefined && orgId !== null && orgId.length > 0) {
console.log("Adding org_id from user side")
state += `%26org_id%3d${orgId}`;
}else{
state += `%26org_id%3d${org_id}`
}
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
@@ -466,7 +467,7 @@ const AuthenticationOauth2 = (props) => {
state += `%26refresh_uri%3d${authentication_url}`
}
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
if (workflow?.org_id !== undefined && workflow?.org_id !== null && workflow?.org_id.length > 0) {
state += `%26org_id%3d${workflow.org_id}`
}
@@ -696,7 +697,7 @@ const AuthenticationOauth2 = (props) => {
justifyContent: "flex-start",
backgroundColor: "#ffffff",
color: "#2f2f2f",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
minWidth: 300,
maxWidth: 300,
maxHeight: 50,
@@ -721,7 +722,7 @@ const AuthenticationOauth2 = (props) => {
<span style={{display: "flex"}}>
<img
alt={selectedAction.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={selectedAction.large_image}
/>
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 5,}} variant="body1">
@@ -803,7 +804,7 @@ const AuthenticationOauth2 = (props) => {
</span>
: null}
{/*<TextField
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}}
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius,}}
InputProps={{
style:{
},
@@ -835,13 +836,14 @@ const AuthenticationOauth2 = (props) => {
setOauthUrl(data.value);
}
const defaultValue = data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) && authenticationType.type === "oauth2-app" ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value
const isNormalOauth = authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null && authenticationType.redirect_uri.length > 0
const defaultValue = !isNormalOauth && data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) && authenticationType.type === "oauth2-app" ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value
const fieldname = data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name
const fieldname = !isNormalOauth && data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name
return (
<div key={index} style={{ marginTop: authenticationType.type === "oauth2-app" ? 10 : 0, }}>
<div key={index} style={{ marginTop: !isNormalOauth && authenticationType.type === "oauth2-app" ? 10 : 0, }}>
<LockOpenIcon style={{ marginRight: 10 }} />
<b>{fieldname}</b>
@@ -893,7 +895,7 @@ const AuthenticationOauth2 = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -924,7 +926,7 @@ const AuthenticationOauth2 = (props) => {
style={{
marginTop: 20,
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -942,7 +944,7 @@ const AuthenticationOauth2 = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginBottom: 10,
}}
InputProps={{
@@ -964,7 +966,7 @@ const AuthenticationOauth2 = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -982,7 +984,7 @@ const AuthenticationOauth2 = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginBottom: 10,
}}
InputProps={{
@@ -1061,7 +1063,7 @@ const AuthenticationOauth2 = (props) => {
style={{
marginBottom: 40,
marginTop: 20,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
disabled={
clientSecret.length === 0 || clientId.length === 0 || buttonClicked || (allscopes.length !== 0 && selectedScopes.length === 0)
@@ -1092,7 +1094,7 @@ const AuthenticationOauth2 = (props) => {
<Button
style={{
marginLeft: 10,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
disabled={clientSecret.length === 0 || clientId.length === 0}
variant="text"
@@ -438,7 +438,7 @@ const OrgHeaderexpanded = (props) => {
style={{
backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
onChange={(event, newValue) => {
console.log("Found value: ", newValue)
@@ -470,7 +470,7 @@ const OrgHeaderexpanded = (props) => {
<Tooltip arrow placement="left" title={
<span style={{}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
: null}
<Typography>
Choose {data.name}
@@ -498,7 +498,7 @@ const OrgHeaderexpanded = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
{...params}
label="Find a notification workflow"
+3 -1
View File
@@ -11,7 +11,9 @@ const PaperComponent = (props) => {
handle="#draggable-dialog-title"
cancel={'[class*="MuiDialogContent-root"]'}
>
<Paper {...props} />
<Paper
{...props}
/>
</Draggable>
)
}
+85 -37
View File
@@ -207,6 +207,53 @@ const ParsedAction = (props) => {
}
}, [expansionModalOpen])
useEffect(() => {
// Changes the order of params to show in order:
// auth, required, optional
var changed = false
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
return
}
var auth = []
var required = []
var optional = []
var keyorder = []
for (let paramkey in selectedActionParameters) {
const param = selectedActionParameters[paramkey]
keyorder.push(param.name)
if (param.configuration) {
auth.push(param)
continue
}
if (param.required) {
required.push(param)
continue
}
optional.push(param)
}
// Check new keyorder
const newparams = auth.concat(required).concat(optional)
var newkeyorder = []
for (let paramkey in newparams) {
newkeyorder.push(newparams[paramkey].name)
}
if (keyorder.join(",") !== newkeyorder.join(",")) {
//toast("Changed order of params")
setSelectedActionParameters(newparams)
selectedAction.parameters = newparams
setSelectedAction(selectedAction)
}
}, [selectedActionParameters])
useEffect(() => {
if (selectedActionEnvironment === undefined || selectedActionEnvironment === null || Object.keys(selectedActionEnvironment).length === 0) {
@@ -582,8 +629,9 @@ const ParsedAction = (props) => {
}
let newParameters = selectedAction?.parameters?.map((param) => {
let paramvalue = param.value;
let paramvalue = param.value === undefined || param.value === null ? "" : param.value;
let errorVars = [];
if(paramvalue.includes("$")){
let actions = workflow.actions?.map((action) => {
return "$"+action.label?.toLowerCase();
@@ -1161,7 +1209,7 @@ const ParsedAction = (props) => {
var helperText = ""
if (name.includes("url")) {
if (value.includes("localhost") || value.includes("127.0.0.1")) {
helperText = "Can't use localhost. Please change to your external IP."
helperText = "Can't use localhost in Shuffle. Please change to server's IP."
}
}
@@ -1431,7 +1479,7 @@ const ParsedAction = (props) => {
)
if (foundResult === undefined || foundResult === null) {
continue;
continue
}
const oldstartnode = cy.getElementById(selectedAction.id);
@@ -1444,12 +1492,12 @@ const ParsedAction = (props) => {
setSelectedResult(foundResult);
if (setCodeModalOpen !== undefined) {
setCodeModalOpen(true);
setCodeModalOpen(true)
found = true
}
break;
break
}
if (!found) {
@@ -1497,13 +1545,13 @@ const ParsedAction = (props) => {
}}
disabled={autoCompleting}
onClick={() => {
//if (setAiQueryModalOpen !== undefined) {
// setAiQueryModalOpen(true)
//} else {
if (setAiQueryModalOpen !== undefined) {
setAiQueryModalOpen(true)
} else {
aiSubmit("Fill based on previous values", undefined, undefined, selectedAction)
//}
setAutocompleting(true)
}
setAutocompleting(true)
setTimeout(() => {
setAutocompleting(false)
}, 3000)
@@ -1511,7 +1559,7 @@ const ParsedAction = (props) => {
>
<Tooltip
color="primary"
title={"Autocomplete fields. Uses the name of the current action, the fields and previous actions' results"}
title={"Autocomplete the action"}
placement="top"
>
{autoCompleting ?
@@ -1568,7 +1616,7 @@ const ParsedAction = (props) => {
color: "white",
height: 35,
marginleft: 10,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
SelectDisplayProps={{
style: {
@@ -1855,7 +1903,9 @@ const ParsedAction = (props) => {
<span>
<Button
color="primary"
style={{}}
style={{
textTransform: "none",
}}
fullWidth
variant="contained"
onClick={() => {
@@ -1963,7 +2013,7 @@ const ParsedAction = (props) => {
color: "white",
height: 50,
maxWidth: rightsidebarStyle.maxWidth - 80,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
<MenuItem
@@ -2066,7 +2116,7 @@ const ParsedAction = (props) => {
: null}
{showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
{/*showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
<div style={{ marginTop: "20px" }}>
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Environment</Typography>
<Select
@@ -2097,7 +2147,7 @@ const ParsedAction = (props) => {
backgroundColor: theme.palette.inputColor,
color: "white",
height: "50px",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
{environments.map((data, index) => {
@@ -2153,15 +2203,9 @@ const ParsedAction = (props) => {
})}
</Select>
{/*selectedActionEnvironment.running_ip === "" && selectedActionEnvironment.Name !== "Cloud" && selectedActionEnvironment.Name !== "cloud" ?
<a href={`/admin?tab=environment&env=${selectedActionEnvironment.Name}`} target="_blank" style={{textDecoration: "none", color: "#f85a3e",}}>
<Typography style={{}}>
Configure the environment
</Typography>
</a>
: null*/}
</div>
) : null}
) : null*/}
{workflow.execution_variables !== undefined &&
workflow.execution_variables !== null &&
workflow.execution_variables.length > 0 ? (
@@ -2201,7 +2245,7 @@ const ParsedAction = (props) => {
backgroundColor: theme.palette.inputColor,
color: "white",
height: "50px",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
<MenuItem
@@ -2294,7 +2338,7 @@ const ParsedAction = (props) => {
style={{
backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
onChange={(event, newValue) => {
// Workaround with event lol
@@ -2476,7 +2520,7 @@ const ParsedAction = (props) => {
variant="body1"
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
label={isIntegration ? "Choose a category" : "Find Actions"}
variant="outlined"
@@ -2496,7 +2540,7 @@ const ParsedAction = (props) => {
value={selectedAction.name}
fullWidth
onChange={setNewSelectedAction}
style={{backgroundColor: theme.palette.inputColor, color: "white", height: 50, borderRadius: theme.palette.borderRadius,}}
style={{backgroundColor: theme.palette.inputColor, color: "white", height: 50, borderRadius: theme.palette?.borderRadius,}}
SelectDisplayProps={{
style: {
marginLeft: 10,
@@ -2724,7 +2768,7 @@ const ParsedAction = (props) => {
style={{
backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
onChange={(event, newValue) => {
console.log("SELECT: ", event, newValue)
@@ -2786,7 +2830,7 @@ const ParsedAction = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
{...params}
label="Find App to Translate"
@@ -2801,7 +2845,7 @@ const ParsedAction = (props) => {
<div
style={{
border: "1px solid rgba(255,255,255,0.6)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginTop: 15,
marginBottom: 10,
maxHeight: 70,
@@ -2838,7 +2882,7 @@ const ParsedAction = (props) => {
if (selectedAction.parameters === undefined || selectedAction.parameters === null || selectedAction.parameters.length !== selectedActionParameters.length) {
//selectedAction.parameters = selectedActionParameters
console.log("PARAM BUG: ", selectedAction)
//console.log("PARAM BUG - length change(?): ", selectedAction)
}
//!selectedAction.auth_not_required &&
@@ -3008,7 +3052,7 @@ const ParsedAction = (props) => {
marginTop: 50,
border: "1px solid rgba(255,255,255,0.7)",
borderTop: "1px solid rgba(255,255,255,0.7)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
alignItems: "center",
textAlign: "center",
}}
@@ -3284,11 +3328,13 @@ const ParsedAction = (props) => {
}}
>
<TextField
autofill="off"
autoComplete="off"
id={clickedFieldId}
disabled={disabled}
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
border:
selectedActionParameters[count].required ||
selectedActionParameters[count].configuration
@@ -3323,6 +3369,8 @@ const ParsedAction = (props) => {
"field_number": count,
"actionlist": actionlist,
"field_id": clickedFieldId,
"example": selectedActionParameters[count].example,
})
}}
/>
@@ -3646,7 +3694,7 @@ const ParsedAction = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
endAdornment: hideExtraTypes ? null : (
@@ -3730,7 +3778,7 @@ const ParsedAction = (props) => {
backgroundColor: theme.palette.surfaceColor,
color: "white",
height: "50px",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
{parsedoptions.map(
@@ -4307,7 +4355,7 @@ const ParsedAction = (props) => {
color: "white",
height: 50,
marginTop: 2,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
onChange={(e) => {
console.log("SELECT ONCHANGE DONE")
+1 -1
View File
@@ -286,7 +286,7 @@ const Priorities = (props) => {
borderBottom: "1px solid rgba(255,255,255,0.4)",
marginBottom: 20,
border: highlighted ? "2px solid #f85a3e" : null,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
<div style={{display: "flex", }}>
+3 -3
View File
@@ -121,7 +121,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 ? "#1A1A1A": 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, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: clickedFromOrgTab ? "#1A1A1A": 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, }}/> : null}
@@ -131,7 +131,7 @@ const Priority = (props) => {
</span>
{priority.type === "usecase" && priority.description.includes("&") ?
<span style={{display: "flex", marginTop: 10, }}>
<img src={newdescription.split("&")[1]} alt={priority.name} style={{height: srcSize, width: srcSize, marginRight: realignedSrc ? isCloud ? 0 : -10 : 10, borderRadius: theme.palette.borderRadius-3, marginTop: realignedSrc ? 5 : 0 }} />
<img src={newdescription.split("&")[1]} alt={priority.name} style={{height: srcSize, width: srcSize, marginRight: realignedSrc ? isCloud ? 0 : -10 : 10, borderRadius: theme.palette?.borderRadius-3, marginTop: realignedSrc ? 5 : 0 }} />
<Typography variant="body2" color="textSecondary" style={{marginTop: 3, }}>
{newdescription.split("&")[0]}
</Typography>
@@ -139,7 +139,7 @@ const Priority = (props) => {
{newdescription.split("&").length > 3 ?
<span style={{display: "flex", }}>
<ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, }}/>
<img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: dstSize, width: dstSize, marginRight: realignedDst ? -5 : 10, borderRadius: theme.palette.borderRadius-3, marginTop: realignedDst ? 5 : 0 }} />
<img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: dstSize, width: dstSize, marginRight: realignedDst ? -5 : 10, borderRadius: theme.palette?.borderRadius-3, marginTop: realignedDst ? 5 : 0 }} />
<Typography variant="body2" color="textSecondary" style={{marginTop: 3}}>
{newdescription.split("&")[2]}
</Typography>
+10 -12
View File
@@ -1,5 +1,6 @@
import React from "react"
import { Link } from "react-router-dom";
import {
Avatar,
Box,
@@ -20,7 +21,6 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
const navigate = useNavigate();
const [hovered, setHovered] = React.useState(false)
if (workflow === undefined || workflow === null) {
console.log("No workflow")
return null
@@ -39,12 +39,11 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
// Check if workflow.input_markdown has an image in it
// If it does, show it as the main thing
//
var relevantImageUrl = ""
if (workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown !== "") {
if (workflow?.form_control?.input_markdown !== undefined && workflow?.form_control?.input_markdown !== null && workflow?.form_control?.input_markdown !== "") {
// Look for <img> tag or ![alt](src) markdown
// html > markdown
const imgTag = workflow.input_markdown.match(/<img[^>]+>/g)
const imgTag = workflow?.form_control?.input_markdown.match(/<img[^>]+>/g)
if (imgTag !== null) {
const src = imgTag[0].match(/src="([^"]+)"/)
@@ -52,7 +51,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
relevantImageUrl = src[1]
}
} else {
const markdownTag = workflow.input_markdown.match(/!\[.*\]\(.*\)/g)
const markdownTag = workflow?.form_control?.input_markdown.match(/!\[.*\]\(.*\)/g)
if (markdownTag !== null) {
const src = markdownTag[0].match(/\(([^)]+)\)/)
@@ -68,15 +67,13 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<Link to={`/workflows/` + workflow?.id} style={{textDecoration: "none"}}>
<Button
onClick={() => {
onClick={(e) => {
if (onclickHandler !== undefined) {
e.preventDefault()
e.stopPropagation()
onclickHandler()
} else {
navigate(`/workflows/` + workflow?.id)
setTimeout(() => {
window.location.reload()
}, 100)
}
}}
style={{
@@ -89,7 +86,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.1s",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
backgroundColor: hovered || currentWorkflowId === workflow.id ? "#1f1f1f" : "transparent",
}}
disableRipple
@@ -150,6 +147,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
}
</Box>
</Button>
</Link>
</div>
)
}
+31 -17
View File
@@ -35,6 +35,7 @@ import {
PlayArrow as PlayArrowIcon,
Insights as InsightsIcon,
Replay as ReplayIcon,
EditNote as EditNoteIcon,
} from '@mui/icons-material';
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
@@ -76,6 +77,10 @@ const RuntimeDebugger = (props) => {
{"id": "", "name": "All Workflows",}
])
if (document != undefined) {
document.title = "Workflow Run Debugger"
}
// Shitty workflow search on purpose :)
const handleWorkflowUsageCount = (workflows) => {
if (workflows === undefined || workflows === null || workflows.length === 0) {
@@ -219,7 +224,7 @@ const RuntimeDebugger = (props) => {
}
const getAvailableWorkflows = () => {
const getAvailableWorkflows = (workflowId) => {
fetch(globalUrl + "/api/v1/workflows", {
method: "GET",
headers: {
@@ -240,6 +245,15 @@ const RuntimeDebugger = (props) => {
var foundWorkflows = [{"id": "", "name": "All Workflows",}]
foundWorkflows.push(...responseJson)
setWorkflows(foundWorkflows)
if (workflowId !== undefined && workflowId !== null && workflowId !== "" && workflowId.length === 36) {
for (var key in responseJson) {
if (responseJson[key].id === workflowId) {
setWorkflow(responseJson[key])
break
}
}
}
}
})
.catch((error) => {
@@ -248,7 +262,6 @@ const RuntimeDebugger = (props) => {
}
useEffect(() => {
getAvailableWorkflows()
// Find workflow_id in url query
const urlParams = new URLSearchParams(window.location.search);
@@ -265,6 +278,8 @@ const RuntimeDebugger = (props) => {
}
}
getAvailableWorkflows(workflowId)
const foundStatus = urlParams.get('status');
if (foundStatus !== undefined && foundStatus !== null && foundStatus !== "") {
setStatus(foundStatus)
@@ -314,15 +329,17 @@ const RuntimeDebugger = (props) => {
var source = params.row.execution_source
if (source === "schedule") {
foundSource = <img src={alltriggers[1].large_image} alt="schedule" style={{borderRadius: theme.palette.borderRadius, height: imageSize, width: imageSize, }} />
foundSource = <img src={alltriggers[1].large_image} alt="schedule" style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
} else if (source === "webhook") {
foundSource = <img src={alltriggers[0].large_image} alt="webhook" style={{borderRadius: theme.palette.borderRadius, height: imageSize, width: imageSize, }} />
foundSource = <img src={alltriggers[0].large_image} alt="webhook" style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
} else if (source === "subflow" || source.length === 36) {
foundSource = <img src={alltriggers[4].large_image} alt="subflow" style={{borderRadius: theme.palette.borderRadius, height: imageSize, width: imageSize, }} />
foundSource = <img src={alltriggers[3].large_image} alt="subflow" style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
source = "subflow"
} else if (source === "rerun" || source.length === 36) {
foundSource = <ReplayIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} />
source = "rerun of a previous run"
} else if (source === "form") {
foundSource = <EditNoteIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} />
} else {
source = "manual"
}
@@ -888,13 +905,13 @@ const RuntimeDebugger = (props) => {
{userdata.support === true ?
<Button
variant={ignoreOrg ? "contained" : "outlined"}
color="primary"
style={{maxHeight: 40, marginTop: 25, }}
color="secondary"
style={{marginLeft: 100, maxHeight: 40, marginTop: 25, }}
onClick={() => {
setIgnoreOrg(!ignoreOrg)
}}
>
{ignoreOrg ? "Ignoring Org" : "Ignore Org"}
{ignoreOrg ? "Ignoring Org" : "Ignore Org (Support Only)"}
</Button>
: null}
</div>
@@ -942,11 +959,8 @@ const RuntimeDebugger = (props) => {
},
}}
getOptionLabel={(option) => {
if (
option === undefined ||
option === null ||
option.name === undefined ||
option.name === null
if (option === undefined || option === null ||
option.name === undefined || option.name === null
) {
return "No Workflow Selected";
}
@@ -961,7 +975,7 @@ const RuntimeDebugger = (props) => {
style={{
backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginTop: 5,
marginLeft: 5,
}}
@@ -995,13 +1009,13 @@ const RuntimeDebugger = (props) => {
<Tooltip arrow placement="left" title={
<span style={{}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
: null}
<Typography>
Choose {data.name}
</Typography>
</span>
} placement="bottom">
}>
<MenuItem
style={{
backgroundColor: theme.palette.inputColor,
@@ -1023,7 +1037,7 @@ const RuntimeDebugger = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
{...params}
label="Workflow"
+8 -6
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, useContext } from 'react';
import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
@@ -33,6 +33,8 @@ import {
AvatarGroup,
} from "@mui/material"
import { Context } from '../context/ContextApi.jsx';
import { Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'
import algoliasearch from 'algoliasearch/lite';
@@ -47,8 +49,9 @@ const chipStyle = {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const SearchData = props => {
const { serverside, globalUrl, userdata, searchBarModalOpen, setSearchBarModalOpen } = props
const { serverside, globalUrl, userdata } = props
let navigate = useNavigate();
const { searchBarModalOpen, setSearchBarModalOpen } = useContext(Context);
const borderRadius = 3
const node = useRef()
const [searchOpen, setSearchOpen] = useState(false)
@@ -547,8 +550,6 @@ const SearchData = props => {
e.preventDefault()
e.stopPropagation()
console.log("OBJECT CHANGE: ", hit.objectID)
// This does nothing rofl
if (userdata.active_apps === undefined || userdata.active_apps === null) {
activateApp(hit.name, hit.objectID, "activate")
@@ -873,10 +874,11 @@ const SearchData = props => {
</List>
</Grid>
<Grid style={{ textAlign: "end", width: "100%", textTransform: 'capitalize', }}>
<Button style={{ textAlign: "center", textTransform: 'capitalize' }}
onClick={() => { window.location = "/search"; }} >
<Link to="/search" style={{ textDecoration: "none", color: "#f85a3e" }}>
<Button style={{ textAlign: "center", textTransform: 'capitalize' }}>
See More
</Button>
</Link>
</Grid>
</Grid>
) : null
+3 -3
View File
@@ -4,7 +4,7 @@ import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import SearchBox from "../components/SearchData.jsx";
import { Context } from '../context/contextApi.jsx';
import { Context } from '../context/ContextApi.jsx';
import {
Chip,
@@ -101,7 +101,7 @@ const SearchField = props => {
</div>
: null}
<DialogContent className='dialog-content' style={{}}>
<SearchBox globalUrl={globalUrl} setSearchBarModalOpen={setSearchBarModalOpen} modalOpen={searchBarModalOpen} serverside={serverside} userdata={userdata} />
<SearchBox globalUrl={globalUrl} serverside={serverside} userdata={userdata} />
</DialogContent>
<Divider style={{overflow: "hidden"}}/>
<span style={{display:"flex", width:"100%", height:30}}>
@@ -131,7 +131,7 @@ const SearchField = props => {
<div style={{ marginTop: "auto", marginLeft: !isLoggedIn ? 0: "auto", marginRight: !isLoggedIn ? 0 : "auto", width: !isLoggedIn ? "auto" : 410, }}>
{modalView}
<TextField
style={{ backgroundColor: "#212121", height: 48, borderRadius: rounded === true ? 25 : theme.palette.borderRadius, minWidth: fieldWidth, maxWidth: fieldWidth, }}
style={{ backgroundColor: "#212121", height: 48, borderRadius: rounded === true ? 25 : theme.palette?.borderRadius, minWidth: fieldWidth, maxWidth: fieldWidth, }}
InputProps={{
style: {
color: "white",
+85 -18
View File
@@ -40,6 +40,7 @@ import {
Close as CloseIcon,
DragIndicator as DragIndicatorIcon,
RestartAlt as RestartAltIcon,
} from '@mui/icons-material';
@@ -56,6 +57,7 @@ import { tags as t } from '@lezer/highlight';
import AceEditor from "react-ace";
import ace from "ace-builds";
import 'ace-builds/src-noconflict/mode-python';
import 'ace-builds/src-noconflict/mode-json';
//import 'ace-builds/src-noconflict/theme-twilight';
//import 'ace-builds/src-noconflict/theme-solarized_dark';
import 'ace-builds/src-noconflict/theme-gruvbox';
@@ -109,6 +111,9 @@ const CodeEditor = (props) => {
setActiveDialog,
fieldname,
contentLoading,
editorData,
setAiQueryModalOpen,
} = props
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
@@ -163,6 +168,11 @@ const CodeEditor = (props) => {
setMenuPosition(null);
}
useEffect(() => {
highlight_variables(localcodedata)
expectedOutput(localcodedata)
}, [localcodedata])
let navigate = useNavigate();
useEffect(() => {
@@ -811,11 +821,7 @@ const CodeEditor = (props) => {
}
const handleItemClick = (values) => {
if (
values === undefined ||
values === null ||
values.length === 0
) {
if (values === undefined || values === null || values.length === 0) {
return;
}
@@ -830,7 +836,11 @@ const CodeEditor = (props) => {
toComplete += values[key].autocomplete;
}
setlocalcodedata(localcodedata+toComplete)
handleClick({
"value": toComplete
})
//setlocalcodedata(localcodedata+toComplete)
setMenuPosition(null)
}
@@ -839,11 +849,38 @@ const CodeEditor = (props) => {
return
}
// Injects it in the right spot instead of random
var edited = false
if (currentCharacter !== undefined && currentCharacter !== null && currentCharacter !== -1 && currentLine !== undefined && currentLine !== null && currentLine !== -1) {
// Input at the right spot
var codedatasplit = localcodedata.split('\n')
if (codedatasplit.length > currentLine) {
var currentLineData = codedatasplit[currentLine]
// Remove newlines from item.value
if (item.value.includes("% python %")) {
item.value = item.value.replaceAll("\n", ";")
item.value = item.value.replaceAll("python %};", "python %}")
} else {
item.value = item.value.replaceAll("\n", "")
}
currentLineData = currentLineData.slice(0, currentCharacter) + item.value + currentLineData.slice(currentCharacter)
codedatasplit[currentLine] = currentLineData
setlocalcodedata(codedatasplit.join('\n'))
edited = true
}
}
if (edited === false) {
if (!item.value.includes("{%") && !item.value.includes("{{")) {
setlocalcodedata(localcodedata+" | "+item.value+" }}")
} else {
setlocalcodedata(localcodedata+item.value)
}
}
setAnchorEl(null)
setAnchorEl2(null)
@@ -946,14 +983,19 @@ const CodeEditor = (props) => {
// Define a custom completer for the Ace Editor
const customVariables = availableVariables
const customCompleter = {
getCompletions: function(editor, session, pos, prefix, callback) {
callback(null, customVariables.map((variable) => ({
console.log("CUSTOM COMPLETER: ", prefix)
callback(null, availableVariables.map((variable) => {
console.log("CUSTOM VAR: ", variable)
return ({
caption: variable,
value: variable,
meta: 'custom',
})));
})
}))
}
}
@@ -1338,6 +1380,8 @@ const CodeEditor = (props) => {
onClick={() => {
console.log("CLICKED: ", innerdata);
console.log(innerdata.example)
//const handleClick = (item) => {
handleItemClick([innerdata]);
}}
>
@@ -1482,14 +1526,37 @@ const CodeEditor = (props) => {
width: 50,
marginLeft: 100,
}}
disabled={isAiLoading}
disabled={editorData === undefined || editorData.example === undefined || editorData.example === null || editorData.example.length === 0}
onClick={() => {
setlocalcodedata(editorData.example)
}}
color="secondary"
>
<Tooltip
title={"Reset to example body"}
placement="top"
>
<RestartAltIcon />
</Tooltip>
</IconButton>
<IconButton
style={{
height: 50,
width: 50,
marginLeft: 0,
}}
disabled={isAiLoading || editorData.name !== "body"}
onClick={() => {
if (setAiQueryModalOpen !== undefined) {
setAiQueryModalOpen(true)
} else {
autoFormat(localcodedata)
}
}}
>
<Tooltip
color="primary"
title={"Auto format data"}
title={"Format with AI"}
placement="top"
>
{isAiLoading ?
@@ -1504,7 +1571,7 @@ const CodeEditor = (props) => {
}
<div style={{
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
position: "relative",
paddingTop: 0,
// minHeight: 548,
@@ -1512,8 +1579,10 @@ const CodeEditor = (props) => {
}}>
{(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor ? (
<AceEditor
id="shuffle-codeeditor"
name="shuffle-codeeditor"
value={localcodedata}
mode={selectedAction === undefined ? "" : selectedAction.name === "execute_python" ? "python" : ""}
mode={selectedAction === undefined ? "json" : selectedAction.name === "execute_python" ? "python" : "json"}
theme="gruvbox"
height={isFileEditor ? 450 : 550}
width={isFileEditor ? 650 : "100%"}
@@ -1538,12 +1607,10 @@ const CodeEditor = (props) => {
highlight_variables(localcodedata)
}}
onCursorChange={(cursorPosition, editor, value) => {
setCurrentCharacter(cursorPosition.column)
setCurrentLine(cursorPosition.row)
setCurrentCharacter(cursorPosition.cursor.column)
setCurrentLine(cursorPosition.cursor.row)
findIndex(cursorPosition.row, cursorPosition.column)
//highlight_variables(localcodedata)
//console.log("VALUE CURSOR: ", value)
}}
onChange={(value, editor) => {
// setlocalcodedata(value)
@@ -1666,7 +1733,7 @@ const CodeEditor = (props) => {
padding: 10,
marginTop: -2,
border: `2px solid ${theme.palette.inputColor}`,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
maxHeight: 450,
minHeight: 450,
overflow: "auto",
@@ -102,7 +102,7 @@ const SuggestedWorkflows = (props) => {
placement="top"
style={{ zIndex: 10011 }}
>
<div key={index} style={{cursor: finished ? "auto" : "pointer", marginTop: 10, padding: 10, borderRadius: theme.palette.borderRadius, border: `1px solid ${bordercolor}`, display: "flex", backgroundColor: hovering === true ? theme.palette.inputColor : theme.palette.surfaceColor, }} onMouseOver={() => {
<div key={index} style={{cursor: finished ? "auto" : "pointer", marginTop: 10, padding: 10, borderRadius: theme.palette?.borderRadius, border: `1px solid ${bordercolor}`, display: "flex", backgroundColor: hovering === true ? theme.palette.inputColor : theme.palette.surfaceColor, }} onMouseOver={() => {
setHovering(true)
}} onMouseOut={() => {
setHovering(false)
@@ -140,7 +140,7 @@ const SuggestedWorkflows = (props) => {
//<Paper style={{width: 275, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}>
return (
<Paper style={{margin: "auto", position: "relative", backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette.borderRadius, zIndex: foundZindex, border: "1px solid rgba(255,255,255,0.2)", top: 100, left: 85,}}>
<Paper style={{margin: "auto", position: "relative", backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, zIndex: foundZindex, border: "1px solid rgba(255,255,255,0.2)", top: 100, left: 85,}}>
<Dialog
open={usecaseSearch.length > 0 && usecaseSearchType.length > 0}
onClose={() => {
@@ -193,7 +193,7 @@ const SuggestedWorkflows = (props) => {
apps={apps}
/>
</Dialog>
<div style={{minWidth: 250, maxWidth: 250, padding: 15, borderRadius: theme.palette.borderRadius, position: "relative", }}>
<div style={{minWidth: 250, maxWidth: 250, padding: 15, borderRadius: theme.palette?.borderRadius, position: "relative", }}>
<Typography variant="body1" style={{textAlign: "center"}}>
Suggested Workflows ({finishedUsecases.length}/{usecaseSuggestions.length})
</Typography>
+6 -4
View File
@@ -594,7 +594,7 @@ const UsecaseSearch = (props) => {
width: 30,
height: 30,
border: "2px solid rgba(255,255,255,0.6)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
maxHeight: 30,
maxWidth: 30,
overflow: "hidden",
@@ -616,7 +616,7 @@ const UsecaseSearch = (props) => {
width: 30,
height: 30,
border: "2px solid rgba(255,255,255,0.6)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
maxWidth: 30,
maxHeight: 30,
overflow: "hidden",
@@ -1266,9 +1266,11 @@ const UsecaseSearch = (props) => {
.then((responseJson) => {
if (responseJson.success === false) {
var msgString = "Failed to activate the app"
if (responseJson.reason !== undefined) {
msgString += ": " + responseJson.reason
}
toast(msgString)
} else {
//toast("App activated for your organization! Refresh the page to use the app.")
@@ -1388,7 +1390,7 @@ const UsecaseSearch = (props) => {
<Typography variant="body1" style={{color: "rgba(255,255,255,0.5)", marginRight: 20, marginTop: 13, }}>
{startText}
</Typography>
<div style={{border: `1px solid ${borderColor}`, backgroundColor: theme.palette.surfaceColor, width: miditem === true ? "65%" : "85%", marginLeft: miditem === true ? 125 : 0, borderRadius: expanded ? theme.palette.borderRadius : 50, maxHeight: expanded || hasError ? 500 : 50, minHeight: 50, }}>
<div style={{border: `1px solid ${borderColor}`, backgroundColor: theme.palette.surfaceColor, width: miditem === true ? "65%" : "85%", marginLeft: miditem === true ? 125 : 0, borderRadius: expanded ? theme.palette?.borderRadius : 50, maxHeight: expanded || hasError ? 500 : 50, minHeight: 50, }}>
{selectionOpen === true ?
<AppsearchPopout
@@ -1560,7 +1562,7 @@ const UsecaseSearch = (props) => {
// <b>{defaultSearch}: {allusecases[usecaseIndex].name}</b>
//console.log("UseCase: ", usecases)
return (
<div style={{maxWidth: "100%", minWidth: "100%", border: "1px solid rgba(255,255,255,0)", borderRadius: theme.palette.borderRadius,}}>
<div style={{maxWidth: "100%", minWidth: "100%", border: "1px solid rgba(255,255,255,0)", borderRadius: theme.palette?.borderRadius,}}>
{configureWorkflowModal}
{authenticationModal}
{showTitle !== false && defaultSearch !== undefined ?
+1 -1
View File
@@ -648,7 +648,7 @@ const WelcomeForm = (props) => {
<div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}>
<div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette.borderRadius, }}>
<div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette?.borderRadius, }}>
<ExploreWorkflow
globalUrl={globalUrl}
isLoggedIn={isLoggedIn}
@@ -585,7 +585,7 @@ const WorkflowTemplatePopup = (props) => {
{/*errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
<Tooltip title="Click to explore the workflow" placement="top">
<span
style={{position: "fixed", display: "flex", right: "10%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: "15px 30px 15px 30px", backgroundColor: theme.palette.platformColor, cursor: "pointer", }}
style={{position: "fixed", display: "flex", right: "10%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, padding: "15px 30px 15px 30px", backgroundColor: theme.palette.platformColor, cursor: "pointer", }}
onClick={() => {
// Open in new tab
window.open("/workflows/" + workflow.id, "_blank")
@@ -759,7 +759,7 @@ const WorkflowTemplatePopup = (props) => {
const borderStyle = isHomePage ? null : isHovered && isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? `1px solid ${highlightColor}` : "1px solid rgba(33, 33, 33, 1)"
return (
<div style={{ display: "flex", height: boxHeight, borderRadius: theme.palette.borderRadius, justifyContent: isMobile ? null : "center" }}
<div style={{ display: "flex", height: boxHeight, borderRadius: theme.palette?.borderRadius, justifyContent: isMobile ? null : "center" }}
>
<ModalView />
@@ -319,7 +319,7 @@ const WorkflowValidationTimeline = (props) => {
<div
style={{
padding: "10px 5px 10px 5px",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
border: hovering === true && showHoverForClick === true ? `1px solid ${decidedColor}` : "1px solid rgba(255,255,255,0.0)",
cursor: hovering === true && showHoverForClick === true ? "pointer" : "default",
+37
View File
@@ -0,0 +1,37 @@
import { createContext, useState, useEffect } from 'react';
export const Context = createContext();
export const AppContext =(props) => {
// Left side bar global states
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(false);
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
//Calculate window width
useEffect(() => {
const handleResize = () => {
setWindowWidth(window?.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<Context.Provider value={{
searchBarModalOpen,
setSearchBarModalOpen,
leftSideBarOpenByClick,
setLeftSideBarOpenByClick,
windowWidth
}}>
{props.children}
</Context.Provider>
)
}
-23
View File
@@ -1,23 +0,0 @@
import { createContext, useState } from 'react';
export const Context = createContext();
export const AppContext =(props) => {
// Left side bar global states
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(false);
return (
<Context.Provider value={{
searchBarModalOpen,
setSearchBarModalOpen,
leftSideBarOpenByClick,
setLeftSideBarOpenByClick
}}>
{props.children}
</Context.Provider>
)
}
+27 -2
View File
@@ -13,6 +13,30 @@ const data = [
return elementname
},
"text-valign": "center",
"text-margin-x": function(element) {
// Attempt at bottom-positioning
// Required text-valign: bottom
// FIXME: Disabled for now.
return "15px"
const name = element.data("label")
console.log("Name: ", name)
if (name === null || name === undefined || name == "" || document=== undefined || document === null) {
return "0px"
}
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d')
context.font = '18px Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif'
const textWidth = context.measureText(name).width
return textWidth + "px"
//return -1*(textWidth) + "px"
},
"font-family": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif",
"font-weight": "lighter",
"font-size": "18px",
@@ -23,7 +47,6 @@ const data = [
padding: "10px",
margin: "5px",
"border-width": "1px",
"text-margin-x": "10px",
"z-index": 5001,
},
},
@@ -263,7 +286,9 @@ const data = [
{
selector: "node[?isStartNode]",
css: {
shape: "ellipse",
shape: function(element) {
return "ellipse"
},
"border-color": "#80deea",
width: "80px",
height: "80px",
-1
View File
@@ -1,7 +1,6 @@
import React from "react";
import { createTheme, adaptV4Theme } from "@mui/material/styles";
//const theme = createTheme({
const theme = createTheme(adaptV4Theme({
palette: {
theme: "dark",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -4717,7 +4717,7 @@ const AppCreator = (defaultprops) => {
<TextField
style={{
backgroundColor: inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -5066,7 +5066,7 @@ const AppCreator = (defaultprops) => {
<TextField
style={{
backgroundColor: inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -5106,7 +5106,7 @@ const AppCreator = (defaultprops) => {
<TextField
style={{
backgroundColor: inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
InputProps={{
style: {
@@ -6018,7 +6018,7 @@ const AppCreator = (defaultprops) => {
setBaseUrl(tmpstring);
}}
/>
<div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette.borderRadius, }}>
<div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette?.borderRadius, }}>
<span style={{display: "flex", }}>
<FormControl style={{ }} variant="outlined">
<Typography variant="h6">Authentication</Typography>
+51 -20
View File
@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { useEffect, useContext, memo } from "react";
import { useInterval } from "react-powerhooks";
import theme from '../theme.jsx';
@@ -49,6 +49,7 @@ import {
Folder as FolderIcon,
LibraryBooks as LibraryBooksIcon,
} from "@mui/icons-material";
import { Context } from "../context/ContextApi.jsx";
import {
ForkRight as ForkRightIcon,
@@ -280,6 +281,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
const Apps = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, serverside, } = props;
//const [workflows, setWorkflows] = React.useState([]);
const baseRepository = "https://github.com/frikky/shuffle-apps";
//const alert = useAlert();
@@ -423,7 +425,7 @@ const Apps = (props) => {
minWidth: "100%",
maxWidth: 612.5,
marginBottom: 5,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
color: "white",
backgroundColor: surfaceColor,
cursor: "pointer",
@@ -875,7 +877,7 @@ const Apps = (props) => {
minWidth: viewWidth,
maxWidth: viewWidth,
color: "white",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
backgroundColor: surfaceColor,
//display: "flex",
marginBottom: 10,
@@ -1551,7 +1553,7 @@ const Apps = (props) => {
minHeight: 150,
maxHeight: 150,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}
if (!makeFancy) {
@@ -1829,7 +1831,7 @@ const Apps = (props) => {
}}>
<TextField
fullWidth
style={{ backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, maxWidth: leftBarSize - 20, }}
style={{ backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius, maxWidth: leftBarSize - 20, }}
InputProps={{
style: {
color: "white",
@@ -2185,7 +2187,7 @@ const Apps = (props) => {
</div>
<div style={{ height: 50 }}>
<TextField
style={{ backgroundColor: inputColor, borderRadius: theme.palette.borderRadius, }}
style={{ backgroundColor: inputColor, borderRadius: theme.palette?.borderRadius, }}
InputProps={{
style: {
},
@@ -2236,7 +2238,7 @@ const Apps = (props) => {
minWidth: viewWidth,
maxWidth: viewWidth,
color: "white",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
//display: "flex",
marginBottom: 10,
overflow: "hidden",
@@ -2373,6 +2375,7 @@ const Apps = (props) => {
toast("Hotloading apps from location in .env");
setIsLoading(true);
fetch(globalUrl + "/api/v1/apps/run_hotload", {
method: "POST",
mode: "cors",
headers: {
Accept: "application/json",
@@ -2999,7 +3002,7 @@ const Apps = (props) => {
PaperProps={{
style: {
backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
color: "white",
minWidth: "800px",
minHeight: "320px",
@@ -3091,7 +3094,7 @@ const Apps = (props) => {
PaperProps={{
style: {
backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
color: "white",
minWidth: "800px",
minHeight: "320px",
@@ -3202,22 +3205,50 @@ const Apps = (props) => {
</Dialog>
) : null;
const loadedCheck =
isLoaded && !firstrequest ? (
<div>
const loadedCheck = isLoaded && !firstrequest ? (
<SidebarAdjustWrapper userdata={userdata}>
<AppsWrapper
userdata={userdata}
appView={appView}
modalView={modalView}
publishModal={publishModal}
generateAppView={generateAppView}
appsModalLoad={appsModalLoad}
deleteModal={deleteModal}
/>
</SidebarAdjustWrapper>
) : (
<div></div>
);
return loadedCheck;
};
export default Apps;
const AppsWrapper = memo(({ appView, modalView, userdata, publishModal, generateAppView, appsModalLoad, deleteModal }) => (
<>
{appView}
{modalView}
{publishModal}
{generateAppView}
{appsModalLoad}
{deleteModal}
</>
));
const SidebarAdjustWrapper = memo(({ userdata, children }) => {
const {leftSideBarOpenByClick } = useContext(Context)
const marginLeft = userdata?.support
? leftSideBarOpenByClick ? 250 : 80
: 0;
return (
<div style={{ marginLeft, transition: 'margin-left 0.3s ease' }}>
{children}
</div>
) : (
<div></div>
);
// Maybe use gridview or something, idk
return loadedCheck;
};
export default Apps;
});
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -197,7 +197,7 @@ const LineChartWrapper = ({keys, height, width}) => {
const name = data.metadata !== undefined && data.metadata.name !== undefined ? data.metadata.name : "No"
return (
<div style={{borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<div style={{borderRadius: theme.palette?.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<Typography variant="body1">
{name}
</Typography>
@@ -281,7 +281,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
}}
content={(data, color) => {
return (
<div style={{borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<div style={{borderRadius: theme.palette?.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<Typography variant="body1">
{data.x}
</Typography>
@@ -795,7 +795,7 @@ const Dashboard = (props) => {
color: "white",
height: 40,
maxWidth: 150,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
{data.available_keys.map((foundKey, index) => {
+44 -16
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useLayoutEffect, useRef, useState } from "react"
import React, { useEffect, useLayoutEffect, useRef, useState, useContext, memo } from "react"
import { toast } from 'react-toastify';
import Markdown from 'react-markdown'
import theme from '../theme.jsx';
@@ -8,6 +8,7 @@ import { BrowserView, MobileView } from "react-device-detect";
import { useParams, useNavigate, Link } from "react-router-dom";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import remarkGfm from 'remark-gfm'
import { Context } from "../context/ContextApi.jsx";
import {
Grid,
TextField,
@@ -166,7 +167,7 @@ export const Img = (props) => {
return(
<img
style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, width: width, maxWidth: width, margin: "auto", marginTop: 10, marginBottom: 10, }}
style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: width, maxWidth: width, margin: "auto", marginTop: 10, marginBottom: 10, }}
alt={props.alt}
src={props.src}
/>
@@ -213,7 +214,7 @@ export const CodeHandler = (props) => {
backgroundColor: theme.palette.inputColor,
overflowY: "auto",
// Have it inline
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
>
{validate.valid === true ?
@@ -250,10 +251,8 @@ export const CodeHandler = (props) => {
}
const Docs = (defaultprops) => {
const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops;
const { globalUrl, selectedDoc, serverside, serverMobile, userdata } = defaultprops;
let navigate = useNavigate();
// Quickfix for react router 5 -> 6
const params = useParams();
//var props = JSON.parse(JSON.stringify(defaultprops))
@@ -526,7 +525,7 @@ const Docs = (defaultprops) => {
style={{
backgroundColor: theme.palette.inputColor,
padding: 15,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginBottom: 30,
display: "flex",
}}
@@ -892,7 +891,7 @@ const Docs = (defaultprops) => {
target="_blank"
style={{ textDecoration: "none", color: "inherit", flex: 1, margin: 10, }}
>
<div style={{ cursor: hover ? "pointer" : "default", borderRadius: theme.palette.borderRadius, flex: 1, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, padding: 25, }}
<div style={{ cursor: hover ? "pointer" : "default", borderRadius: theme.palette?.borderRadius, flex: 1, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, padding: 25, }}
onClick={(event) => {
if (link === "" || link === undefined) {
event.preventDefault()
@@ -932,7 +931,7 @@ const Docs = (defaultprops) => {
return (
<Link to={link} style={hrefStyle}>
<div style={{ width: "100%", height: 80, cursor: hover ? "pointer" : "default", borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, }}
<div style={{ width: "100%", height: 80, cursor: hover ? "pointer" : "default", borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, }}
onMouseOver={() => {
setHover(true)
}}
@@ -968,8 +967,8 @@ const Docs = (defaultprops) => {
Documentation
</Typography>
<div style={{ display: "flex", marginTop: 25, }}>
<CustomButton title="Talk to Support" icon=<img src="/images/Shuffle_logo_new.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette.borderRadius, }} /> />
<CustomButton title="Ask the community" icon=<img src="/images/social/discord.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette.borderRadius, }} /> link="https://discord.gg/B2CBzUm" />
<CustomButton title="Talk to Support" icon=<img src="/images/Shuffle_logo_new.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette?.borderRadius, }} /> />
<CustomButton title="Ask the community" icon=<img src="/images/social/discord.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette?.borderRadius, }} /> link="https://discord.gg/B2CBzUm" />
</div>
<div style={{ textAlign: "left" }}>
@@ -1265,15 +1264,44 @@ const Docs = (defaultprops) => {
</div>
);
console.log("docs render")
// Padding and zIndex etc set because of footer in cloud.
const loadedCheck = (
<div style={{ minHeight: 1000, zIndex: 50000, maxWidth: 1920, minWidth: isMobile ? null : 1366, margin: "auto", }}>
<BrowserView>{postDataBrowser}</BrowserView>
<MobileView>{postDataMobile}</MobileView>
</div>
<DocsWrapper userdata={userdata}>
<DocsContent postDataBrowser={postDataBrowser} postDataMobile={postDataMobile}/>
</DocsWrapper>
);
return <div style={{}}>{loadedCheck}</div>;
return <div>{loadedCheck}</div>;
};
export default Docs;
const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
return(
<div>
<BrowserView>{postDataBrowser}</BrowserView>
<MobileView>{postDataMobile}</MobileView>
</div>
)})
const DocsWrapper = memo(({userdata, children })=>{
const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
return (
<div style={{
minHeight: 1000, zIndex: 1,
maxWidth: Math.min(!userdata?.support ? 1920 : leftSideBarOpenByClick ? windowWidth - 300 : windowWidth - 200, 1920),
minWidth: isMobile ? null : userdata?.support ? leftSideBarOpenByClick ? 800 : 900 : null, margin: "auto",
position: userdata?.support && leftSideBarOpenByClick ? "relative" : "static",
left: userdata?.support && leftSideBarOpenByClick ? 120 : userdata?.support && !leftSideBarOpenByClick ? 80 : 0,
marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick && userdata?.support ? 160 : userdata?.support && !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
transition: "left 0.3s ease-in-out, min-width 0.3s ease-in-out, max-width 0.3s ease-in-out, position 0.3s ease-in-out, margin 0.3s ease-in-out, margin-left 0.3s ease"
}}>
{children}
</div>
)
})
+1 -1
View File
@@ -2467,7 +2467,7 @@ const GettingStarted = (props) => {
maxWidth: 1024,
zIndex: 11,
border: "1px solid rgba(255,255,255,0.1)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
textAlign: "center",
overflow: "auto",
}}
+217 -102
View File
@@ -43,6 +43,7 @@ import {
Lock as LockIcon,
LockOpen as LockOpenIcon,
OpenInNew as OpenInNewIcon,
Edit as EditIcon,
} from '@mui/icons-material';
const hrefStyle = {
@@ -50,20 +51,13 @@ const hrefStyle = {
textDecoration: "none"
}
const bodyDivStyle = {
margin: "auto",
width: isMobile? "100%":"500px",
position: "relative",
paddingBottom: 250,
}
const RunWorkflow = (defaultprops) => {
const { globalUrl, userdata, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
let navigate = useNavigate();
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
const [explorerUi, setExplorerUi] = useState(false)
const [message, setMessage] = useState("");
const [workflow, setWorkflow] = React.useState({});
const [executionRequest, setExecutionRequest] = React.useState({});
@@ -80,6 +74,8 @@ const RunWorkflow = (defaultprops) => {
const [sharingOpen, setSharingOpen] = React.useState(false)
const [realtimeMarkdown, setRealtimeMarkdown] = React.useState("")
const [forms, setForms] = React.useState([])
const [boxWidth, setBoxWidth] = React.useState(500)
const [inputQuestions, setInputQuestions] = React.useState([])
const IframeWrapper = (props) => {
var propsCopy = JSON.parse(JSON.stringify(props))
@@ -94,11 +90,21 @@ const RunWorkflow = (defaultprops) => {
if (propsCopy.width === undefined || propsCopy.width === null) {
propsCopy.width = 400
propsCopy.height = "auto"
propsCopy.margin = "auto"
}
return Img(propsCopy)
}
const bodyDivStyle = {
margin: "auto",
width: isMobile? "100%" : boxWidth,
position: "relative",
paddingBottom: 250,
}
const boxStyle = {
color: "white",
padding: "25px 50px 50px 50px",
@@ -112,7 +118,7 @@ const RunWorkflow = (defaultprops) => {
props.match = {}
props.match.params = params
const defaultTitle = workflow.name !== undefined ? workflow.name : "Run Workflow"
const defaultTitle = workflow.name !== undefined ? "Shuffle - Form for " + workflow.name : "Shuffle - Form to Run Workflows"
if (document != undefined && document.title != defaultTitle) {
document.title = defaultTitle
}
@@ -136,6 +142,18 @@ const RunWorkflow = (defaultprops) => {
return true
}
// Check if it's an object or not
if (typeof executionArgument === "string") {
// Make it an object
try {
executionArgument = JSON.parse(executionArgument)
} catch (e) {
console.log("Error parsing execution argument: ", e)
executionArgument = {}
}
}
console.log("EXEC: ", executionArgument)
for (var key in executionArgument) {
if (executionArgument[key] === undefined || executionArgument[key] === null || executionArgument[key] === "") {
return false
@@ -257,9 +275,12 @@ const RunWorkflow = (defaultprops) => {
return (
<div style={{marginTop: executionMargin, }}>
{workflowQuestion !== "" ? null :
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
}
{validate.valid === false ?
{workflowQuestion !== "" ? null :
validate.valid === false ?
<div style={{marginTop: 20, }}>
<Divider />
<Markdown
@@ -446,18 +467,6 @@ const RunWorkflow = (defaultprops) => {
})
}
/*
useEffect(() => {
if (executionRequest === undefined || executionRequest === null || executionRequest.execution_id === undefined || executionRequest.execution_id === null || executionRequest.execution_id.length === 0) {
return
}
if (executionRequest.start === true) {
start()
}
}, [executionRequest])
*/
const { start, stop } = useInterval({
duration: 1500,
startImmediate: true,
@@ -520,8 +529,8 @@ const RunWorkflow = (defaultprops) => {
if (realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0) {
const newmarkdown = realtimeMarkdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown)
} else if (inputWorkflow.input_markdown !== undefined && inputWorkflow.input_markdown !== null && inputWorkflow.input_markdown.length > 0) {
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
} else if (inputWorkflow.form_control.input_markdown !== undefined && inputWorkflow.form_control.input_markdown !== null && inputWorkflow.form_control.input_markdown.length > 0) {
const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown)
}
}
@@ -531,15 +540,21 @@ const RunWorkflow = (defaultprops) => {
console.log("Get workflow error: ", error.toString())
if (realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0) {
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown)
} else if (inputWorkflow.input_markdown !== undefined && inputWorkflow.input_markdown !== null && inputWorkflow.input_markdown.length > 0) {
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
} else if (inputWorkflow.form_control.input_markdown !== undefined && inputWorkflow.form_control.input_markdown !== null && inputWorkflow.form_control.input_markdown.length > 0) {
const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown)
}
})
}
const searchParams = new URLSearchParams(window.location.search)
const answer = searchParams.get("answer")
const execution_id = searchParams.get("reference_execution")
const authorization = searchParams.get("authorization")
const sourceNode = searchParams.get("source_node")
const getWorkflow = (workflow_id, selectedNode) => {
setRealtimeMarkdown("")
@@ -595,6 +610,56 @@ const RunWorkflow = (defaultprops) => {
}
}
// Override with just relevant fields
if (sourceNode !== undefined && sourceNode !== null && sourceNode.length > 0) {
for (var triggerkey in responseJson.triggers) {
const trig = responseJson.triggers[triggerkey]
if (trig.id !== sourceNode) {
continue
}
console.log("TRIG: ", trig)
if (trig.parameters === undefined || trig.parameters === null) {
trig.parameters = []
}
for (var paramkey in trig.parameters) {
const param = trig.parameters[paramkey]
if (param.name !== "input_questions") {
continue
}
// Parse as json
var keepfields = []
try {
const parsed = JSON.parse(param.value)
// Find this in the workflow.input_questions
for (var questionkey in responseJson.input_questions) {
var question = JSON.parse(JSON.stringify(responseJson.input_questions[questionkey]))
question.value = question.value.split(";")[0]
if (parsed.includes(question.name)) {
keepfields.push(question.value)
}
}
//newexec = {}
} catch (e) {
console.log("Error parsing input questions: ", e)
}
// Remapping it to exec
if (keepfields.length > 0) {
newexec = {}
for (var key in keepfields) {
newexec[keepfields[key]] = ""
}
}
}
}
}
setExecutionArgument(newexec)
}
@@ -631,7 +696,8 @@ const RunWorkflow = (defaultprops) => {
}
}
responseJson.input_questions = relevantquestions
setInputQuestions(relevantquestions)
//responseJson.input_questions = relevantquestions
}
}
}
@@ -642,10 +708,10 @@ const RunWorkflow = (defaultprops) => {
}
}
if (responseJson.input_markdown !== undefined && responseJson.input_markdown !== null && responseJson.input_markdown.length > 0) {
if (responseJson.form_control.input_markdown !== undefined && responseJson.form_control.input_markdown !== null && responseJson.form_control.input_markdown.length > 0) {
// Look for {{ uuid }} format, and try to run that workflow with their account
// This is a hack, but a fun one.
var newmarkdown = responseJson.input_markdown.replace("", "")
var newmarkdown = responseJson.form_control.input_markdown.replace("", "")
const uuidRegex = /{{\s[a-f0-9-]+\s}}/g
const found = newmarkdown.match(uuidRegex)
@@ -697,7 +763,21 @@ const RunWorkflow = (defaultprops) => {
handleExecutionLoader()
handleGetOrg(responseJson.org_id)
setWorkflow(responseJson);
if (responseJson.form_control === undefined || responseJson.form_control === null) {
responseJson.form_control = {
"input_markdown": "",
"output_yields": [],
"form_width": 500,
}
}
if (responseJson.form_control.form_width !== undefined && responseJson.form_control.form_width !== null && responseJson.form_control.form_width > 300) {
setBoxWidth(responseJson.form_control.form_width)
}
setWorkflow(responseJson)
})
.catch((error) => {
console.log("Get workflow error: ", error.toString());
@@ -723,7 +803,6 @@ const RunWorkflow = (defaultprops) => {
if (responseJson.result.startsWith("[") && responseJson.result.endsWith("]")) {
try {
responseJson.result = JSON.parse(responseJson.result).length
console.log("Set length to: ", responseJson.result)
} catch (e) {
console.log("Error parsing length: ", e)
}
@@ -875,8 +954,8 @@ const RunWorkflow = (defaultprops) => {
const newmarkdown = realtimeMarkdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
setRealtimeMarkdown(newmarkdown)
} else if (workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0) {
const newmarkdown = workflow.input_markdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
} else if (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) {
const newmarkdown = workflow.form_control.input_markdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
setRealtimeMarkdown(newmarkdown)
}
@@ -893,17 +972,23 @@ const RunWorkflow = (defaultprops) => {
});
};
const searchParams = new URLSearchParams(window.location.search)
const answer = searchParams.get("answer")
const execution_id = searchParams.get("reference_execution")
const authorization = searchParams.get("authorization")
const sourceNode = searchParams.get("source_node")
useEffect(() => {
if (!isLoaded) {
return
}
if (props.match.params.key === undefined) {
setExplorerUi(true)
if (isLoggedIn) {
loadForms(userdata.active_org.id)
handleGetOrg(userdata.active_org.id)
}
return
}
getWorkflow(props.match.params.key, sourceNode)
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
console.log("Get execution: ", execution_id)
@@ -915,7 +1000,6 @@ const RunWorkflow = (defaultprops) => {
}
}, [isLoaded])
useEffect(() => {
if (executionData === undefined || executionData === null || executionData === {}) {
return
@@ -947,9 +1031,14 @@ const RunWorkflow = (defaultprops) => {
}
if (result.status !== "WAITING") {
if (parsedresult.information !== undefined && parsedresult.information !== null && parsedresult.information.length > 0) {
setWorkflowQuestion(parsedresult.information)
}
if (parsedresult.click_info !== undefined && parsedresult.click_info !== null) {
if (parsedresult.click_info.user !== undefined && parsedresult.click_info.user !== null && parsedresult.click_info.user.length > 0) {
setMessage("Already answered by " + parsedresult.click_info.user)
}
} else {
setMessage("Answered.")
@@ -965,8 +1054,12 @@ const RunWorkflow = (defaultprops) => {
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
//const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
const disabledButtons = executionLoading || executionRunning
// Check if all fields are filled in?
var disabledButtons = executionLoading || executionRunning || message.length > 0
if (disabledButtons === false && workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0) {
// Check field values
//disabledButtons = handleValidateForm(executionArgument)
}
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io"
@@ -995,20 +1088,69 @@ const RunWorkflow = (defaultprops) => {
}
}
const FormList = () => {
return (
<div>
{forms.map((form, formIndex) => {
if (form.id === undefined || form.id === null) {
return null
}
return (
<div key={formIndex} style={{marginBottom: 10, }}>
<RecentWorkflow
workflow={form}
onclickHandler={() => {
navigate(`/forms/${form.id}`)
getWorkflow(form.id, sourceNode)
setExplorerUi(false)
}}
currentWorkflowId={workflow.id}
/>
</div>
)
})}
</div>
)
}
const ExplorerUi = () => {
return (
<div style={{paddingTop: 50, marginTop: 50, width: 250, itemAlign: "center", textAlign: "center", margin: "auto", }}>
{forms !== undefined && forms !== null && forms.length > 0 ?
<div>
<Typography variant="h6" style={{marginBottom: 20, }}>
Available forms
</Typography>
<FormList />
</div>
:
<Typography variant="h6" style={{marginTop: 100, marginBottom: 20, }}>
No Form Found
</Typography>
}
</div>
)
}
var validResults = 0
const basedata =
<div style={bodyDivStyle}>
<Paper style={boxStyle}>
{workflow.id === undefined || workflow.id === null ?
{explorerUi === true ?
<ExplorerUi />
:
workflow.id === undefined || workflow.id === null ?
<div style={{paddingTop: 150, marginTop: 150, width: 250, itemAlign: "center", textAlign: "center", margin: "auto", }}>
<CircularProgress />
<Typography variant="body1" style={{marginTop: 20, }}>
Loding Form Details...
Loading Form Details...
</Typography>
</div>
:
<div>
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ?
{workflowQuestion !== "" || (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) ?
<div style={{marginBottom: 20, }}>
<Markdown
components={{
@@ -1024,13 +1166,13 @@ const RunWorkflow = (defaultprops) => {
}}
rehypePlugins={[rehypeRaw]}
>
{realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0 ? realtimeMarkdown : workflow.input_markdown}
{workflowQuestion !== "" ? workflowQuestion : realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0 ? realtimeMarkdown : workflow.form_control.input_markdown}
</Markdown>
</div>
: null}
<form onSubmit={(e) => {onSubmit(e)}} style={{margin: "25px 0px 15px 0px",}}>
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ? null :
{workflowQuestion !== "" || (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) ? null :
<div>
<img
alt={workflow.name}
@@ -1066,7 +1208,7 @@ const RunWorkflow = (defaultprops) => {
<div style={{
backgroundColor: theme.palette.inputColor,
padding: 20,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginBottom: 35,
marginTop: 30,
}}>
@@ -1081,7 +1223,7 @@ const RunWorkflow = (defaultprops) => {
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
<div style={{marginBottom: 5, }}>
{workflow.input_questions.map((question, index) => {
{inputQuestions.map((question, index) => {
// Multiple choice checks for semicolon-splits
var multiChoiceOptions = question.value !== undefined && question.value !== null && question.value.length > 0 && question.value.includes(";") ? question.value.split(";") : []
@@ -1215,25 +1357,26 @@ const RunWorkflow = (defaultprops) => {
What do you want to do?
</Typography>
}
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
onSubmit(null, execution_id, authorization, true)
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
<Button fullWidth id="continue_execution" variant="contained" disabled={!handleValidateForm(executionArgument) || disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
setButtonClicked("FINISHED")
setExecutionData({
status: "FINISHED",
})
onSubmit(null, execution_id, authorization, true)
}}>Continue</Button>
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
&nbsp;or&nbsp;
</Typography>
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => {
onSubmit(null, execution_id, authorization, false)
<Button fullWidth id="abort_execution" variant="contained" disabled={!handleValidateForm(executionArgument) || disabledButtons} color="primary" style={{ flex: 1, }} onClick={() => {
setButtonClicked("ABORTED")
setExecutionData({
status: "ABORTED",
})
onSubmit(null, execution_id, authorization, false)
}}>Stop</Button>
</div>
</span>
@@ -1258,9 +1401,9 @@ const RunWorkflow = (defaultprops) => {
}
{workflow.output_yields !== undefined && workflow.output_yields !== null && workflow.output_yields.length > 0 ?
{workflow.form_control.output_yields !== undefined && workflow.form_control.output_yields !== null && workflow.form_control.output_yields.length > 0 ?
<div style={{marginTop: 20, }}>
{workflow.output_yields.map((yieldItem, index) => {
{workflow.form_control.output_yields.map((yieldItem, index) => {
if (executionData.results === undefined || executionData.results === null || executionData.results.length === 0) {
return null
}
@@ -1328,15 +1471,18 @@ const RunWorkflow = (defaultprops) => {
</div>
}
</Paper>
{workflowQuestion !== "" ? null :
<Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 10, }} >
Forms are in Beta. Form submissions data includes your Organization's unique ID while logged in, or a unique identifier for your browser otherwise. Your input will be automatically sanitized.
Forms are in late Beta. Form submission data includes your Organization's unique ID while logged in, or a unique identifier for your browser otherwise. Your input will be automatically sanitized.
</Typography>
}
</div>
// const isCorrectOrg = userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id
const loadedCheck = isLoaded ?
<div>
<div style={{marginTop: 30, }}>
{editWorkflowModalOpen === true ?
<EditWorkflow
saveWorkflow={saveWorkflow}
@@ -1350,6 +1496,8 @@ const RunWorkflow = (defaultprops) => {
expanded={true}
setRealtimeMarkdown={setRealtimeMarkdown}
boxWidth={boxWidth}
setBoxWidth={setBoxWidth}
scrollTo={"form_fill"}
/>
: null}
@@ -1367,7 +1515,7 @@ const RunWorkflow = (defaultprops) => {
minHeight: 400,
maxHeight: 400,
padding: 25,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
},
@@ -1418,7 +1566,7 @@ const RunWorkflow = (defaultprops) => {
</DialogContent>
</Dialog>
{isLoggedIn && userdata?.active_org?.id === workflow?.org_id ?
{isLoggedIn && userdata?.active_org?.id === workflow?.org_id || userdata?.support === true ?
<div style={{position: "fixed", top: 10, right: 20, }}>
<Button
@@ -1462,7 +1610,7 @@ const RunWorkflow = (defaultprops) => {
setEditWorkflowModalOpen(true)
}}
>
Edit Details
<EditIcon style={{marginRight: 5, }} /> Edit Form
</Button>
</div>
: null}
@@ -1476,7 +1624,8 @@ const RunWorkflow = (defaultprops) => {
// Check width
const overlap = window !== undefined && window.innerWidth !== undefined && window.innerWidth < 1300
const formSidebar = !isLoaded || overlap || !(forms !== undefined && forms !== null && forms.length > 1) ? null :
const formSidebar = explorerUi === true || !isLoaded || overlap || !(forms !== undefined && forms !== null && forms.length > 1) ? null :
<div style={{
minWidth: 215,
maxWidth: 215,
@@ -1485,10 +1634,10 @@ const RunWorkflow = (defaultprops) => {
minHeight: 500,
maxHeight: 500,
position: "absolute",
left: 100,
left: 150,
top: 0,
border: "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
padding: 25,
@@ -1497,7 +1646,7 @@ const RunWorkflow = (defaultprops) => {
}}>
{selectedOrganization !== undefined && selectedOrganization !== null ?
<div style={{display: "flex", marginBottom: 20, }}>
<img src={selectedOrganization.image} style={{width: 40, height: 40, borderRadius: theme.palette.borderRadius, }} />
<img src={selectedOrganization.image} style={{width: 40, height: 40, borderRadius: theme.palette?.borderRadius, }} />
<Typography variant="body1" style={{marginTop: 7, marginLeft: 10, }}>
{selectedOrganization.name}
</Typography>
@@ -1506,41 +1655,7 @@ const RunWorkflow = (defaultprops) => {
: null}
{forms !== undefined && forms !== null && forms.length > 0 ?
<div>
{forms.map((form, formIndex) => {
if (form.id === undefined || form.id === null) {
return null
}
return (
<div key={formIndex} style={{marginBottom: 10, }}>
<RecentWorkflow
workflow={form}
onclickHandler={() => {
navigate(`/forms/${form.id}`)
getWorkflow(form.id, sourceNode)
}}
currentWorkflowId={workflow.id}
/>
{/*
<Button
variant="outlined"
color="primary"
fullWidth
style={{textTransform: "none", }}
onClick={() => {
navigate(`/forms/${form.id}`)
getWorkflow(form.id, sourceNode)
}}
>
{form.name}
</Button>
*/}
</div>
)
})}
</div>
<FormList />
:
<div>
No forms loaded
@@ -1557,4 +1672,4 @@ const RunWorkflow = (defaultprops) => {
)
}
export default RunWorkflow;
export default RunWorkflow
+8 -8
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from "react";
import React, { useState, useEffect, useMemo, useContext } from "react";
import theme from "../theme.jsx";
import { isMobile } from "react-device-detect";
@@ -11,6 +11,7 @@ import { Tabs, Tab, setRef } from "@mui/material";
import { styled } from "@mui/material/styles";
import { makeStyles } from '@mui/styles';
import DiscordChat from "../components/DiscordChat.jsx";
import { Context } from "../context/ContextApi.jsx";
import {
Apps as AppsIcon,
@@ -21,16 +22,14 @@ import {
DescriptionOutlined as DescriptionOutlinedIcon,
} from "@mui/icons-material";
import {
Typography
} from "@mui/material"
import {Typography} from "@mui/material";
// Should be different if logged in :|
const Search = (props) => {
const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader } =
props;
let navigate = useNavigate();
const { leftSideBarOpenByClick } = useContext(Context);
const [curTab, setCurTab] = useState(0);
const iconStyle = { marginRight: isHeader ? null : 10 };
@@ -124,14 +123,15 @@ const Search = (props) => {
flex: "1",
marginLeft: isHeader ? null : 10,
marginRight: isHeader ? null : 10,
paddingLeft: isHeader ? null : 30,
paddingRight: isHeader ? null : 30,
paddingBottom: isHeader ? null : 30,
paddingTop: hidemargins === true ? 0 : isHeader ? null : 30,
display: "flex",
flexDirection: "column",
overflowX: "hidden",
minHeight: 400
minHeight: 400,
paddingLeft: leftSideBarOpenByClick ? 250 : 0,
transition: "padding-left 0.3s ease",
};
const views = {
@@ -223,7 +223,7 @@ const Search = (props) => {
margin: isHeader ? null : "auto",
marginTop: hidemargins === true ? 0 : isHeader ? null : 25,
backgroundColor: "rgba(33, 33, 33, 1)",
borderRadius: 8
borderRadius: 8,
}}
value={curTab}
indicatorColor="primary"
+1 -1
View File
@@ -302,7 +302,7 @@ const SetAuthentication = (props) => {
}
return (
<div style={{ padding: 50, border: failed === true ? `1px solid ${red}` : "1px solid rgba(255,255,255,0.6)", borderRadius: theme.palette.borderRadius, width: 500, margin: "auto", marginTop: 50, itemAlign: "center", textAlign: "center",}}>
<div style={{ padding: 50, border: failed === true ? `1px solid ${red}` : "1px solid rgba(255,255,255,0.6)", borderRadius: theme.palette?.borderRadius, width: 500, margin: "auto", marginTop: 50, itemAlign: "center", textAlign: "center",}}>
<Typography
variant="h4"
style={{ marginLeft: "auto", marginRight: "auto", marginTop: 50}}
+5 -5
View File
@@ -627,7 +627,7 @@ const Settings = (props) => {
display: "flex",
flexDirection: "column",
padding: "0px 0px 12px 0px",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
};
const currentOwner = checkOwner(data, userdata);
@@ -636,7 +636,7 @@ const Settings = (props) => {
}
return (
<Grid item xs={4} style={{ borderRadius: theme.palette.borderRadius }}>
<Grid item xs={4} style={{ borderRadius: theme.palette?.borderRadius }}>
<Paper style={innerPaperStyle}>
<img
src={data.image}
@@ -692,7 +692,7 @@ const Settings = (props) => {
);
const landingpageData = (
<div style={{ display: "flex", marginTop: 120 }}>
<div style={{ display: "flex", paddingTop: 120 }}>
<Paper style={boxStyle}>
{imageInfo}
<h2>Settings</h2>
@@ -1065,7 +1065,7 @@ const Settings = (props) => {
<Paper
square
style={{
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
padding: 50,
backgroundColor: theme.palette.inputColor,
}}
@@ -1100,7 +1100,7 @@ const Settings = (props) => {
<Paper
square
style={{
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
padding: 50,
backgroundColor: theme.palette.inputColor,
}}
+8 -8
View File
@@ -441,7 +441,7 @@ const UsecaseListComponent = (props) => {
navigate(`/usecases?selected_object=${fixedName}`)
}
}}>
<Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor, borderRadius: theme.palette.borderRadius, }} onClick={() => {
<Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor, borderRadius: theme.palette?.borderRadius, }} onClick={() => {
}}>
{!selectedItem ?
<div style={{textAlign: "left", position: "relative",}}>
@@ -799,7 +799,7 @@ const UsecaseListComponent = (props) => {
style={{
backgroundColor: theme.palette.inputColor,
height: 50,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
onChange={(event, newValue) => {
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
@@ -856,7 +856,7 @@ const UsecaseListComponent = (props) => {
<Tooltip arrow placement="left" title={
<span style={{}}>
{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, }} />
<img src={data.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
: null}
<Typography>
Choose {newname}
@@ -881,7 +881,7 @@ const UsecaseListComponent = (props) => {
<TextField
style={{
backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}}
{...params}
label="Find your workflows"
@@ -952,8 +952,8 @@ const UsecaseListComponent = (props) => {
target="_blank"
style={{ textDecoration: "none", color: "rgba(255,255,255,0.7)", marginRight: 5, }}
>
<div style={{width: 160, display: "flex", borderRadius: theme.palette.borderRadius, cursor: "pointer", border: highlight ? "2px solid #f86a3e" : "1px solid rgba(255,255,255,0.7)", backgroundColor: theme.palette.inputColor, padding: "0px 0px 15px 15px", overflow: "hidden",}}>
<img src={subdata.image} style={{width: 40, height: 40, borderRadius: theme.palette.borderRadius, marginTop: 15, }} />
<div style={{width: 160, display: "flex", borderRadius: theme.palette?.borderRadius, cursor: "pointer", border: highlight ? "2px solid #f86a3e" : "1px solid rgba(255,255,255,0.7)", backgroundColor: theme.palette.inputColor, padding: "0px 0px 15px 15px", overflow: "hidden",}}>
<img src={subdata.image} style={{width: 40, height: 40, borderRadius: theme.palette?.borderRadius, marginTop: 15, }} />
<Typography variant="body1" style={{lineHeight: "95%", marginLeft: 12, marginTop: marginTop === 0 ? 19 : 25, maxHeight: 34, }}>
{subdata.name}
</Typography>
@@ -983,7 +983,7 @@ const UsecaseListComponent = (props) => {
height: 350,
width: isMobile? null:350,
marginTop: isMobile ? 50:null,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
border: "1px solid rgba(255,255,255,0.3)",
padding: 5,
backgroundColor: theme.palette.backgroundColor,
@@ -1113,7 +1113,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
}}
content={(data, color) => {
return (
<div style={{borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<div style={{borderRadius: theme.palette?.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<Typography variant="body1">
{data.x}
</Typography>
+6 -6
View File
@@ -304,14 +304,14 @@ const Welcome = (props) => {
minWidth: isMobile ? null : 275,
backgroundColor: theme.palette.surfaceColor,
color: "white",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}
const actionObject = {
padding: "25px",
maxHeight: 280,
minHeight: 280,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
}
const imageStyle = {
@@ -340,7 +340,7 @@ const Welcome = (props) => {
const defaultImage = "/images/experienced.png"
const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage
return (
<div style={{ margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}>
<div style={{ margin: "auto", paddingBottom: 150, minHeight: 1500, paddingTop: 50, }}>
{/*
<div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}>
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
@@ -354,7 +354,7 @@ const Welcome = (props) => {
color="primary"
style={{
backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
padding: 12,
border: "1px solid rgba(255,255,255,0.3)",
maxWidth: 500,
@@ -434,7 +434,7 @@ const Welcome = (props) => {
We will use this information to personalize your automation
</Typography> */}
<div style={{display: isMobile ? null : "flex", marginTop: 70, width: isMobile ? 280 : 700, margin: "auto",}}>
<div style={{border: "2px solid #806BFF", borderRadius: theme.palette.borderRadius, }}>
<div style={{border: "2px solid #806BFF", borderRadius: theme.palette?.borderRadius, }}>
<Card style={paperObject} onClick={() => {
if (isCloud) {
ReactGA.event({
@@ -478,7 +478,7 @@ const Welcome = (props) => {
navigate("/workflows?message=Skipped intro")
}}>
<CardActionArea style={actionObject}>
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius*2, marginBottom: experienced_image === defaultImage ? 24 : 2 }} />
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette?.borderRadius*2, marginBottom: experienced_image === defaultImage ? 24 : 2 }} />
<Typography variant="h4" style={{color: "#F1F1F1"}}>
Experienced
</Typography>
+73 -54
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useContext } from "react";
import React, { useEffect, useContext, memo } from "react";
import ReactDOM from "react-dom"
import { makeStyles } from "@mui/styles";
@@ -6,7 +6,7 @@ import { Navigate } from "react-router-dom";
import SecurityFramework from '../components/SecurityFramework.jsx';
import EditWorkflow from "../components/EditWorkflow.jsx"
import Priority from "../components/Priority.jsx";
import { Context } from "../context/ContextApi.jsx";
import { isMobile } from "react-device-detect"
import {
@@ -92,29 +92,31 @@ import theme from "../theme.jsx";
const svgSize = 24;
const imagesize = 22;
const useStyles = makeStyles((theme) => ({
const useStyles = makeStyles(() => {
return {
datagrid: {
border: 0,
"& .MuiDataGrid-columnsContainer": {
backgroundColor:
theme.palette.type === "light" ? "#fafafa" : theme.palette.inputColor,
theme?.palette?.type === "light" ? "#fafafa" : theme?.palette?.inputColor,
},
"& .MuiDataGrid-iconSeparator": {
display: "none",
},
"& .MuiDataGrid-colCell, .MuiDataGrid-cell": {
borderRight: `1px solid ${
theme.palette.type === "light" ? "white" : "#303030"
theme?.palette?.type === "light" ? "white" : "#303030"
}`,
},
"& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": {
borderBottom: `1px solid ${
theme.palette.type === "light" ? "#f0f0f0" : "#303030"
theme?.palette?.type === "light" ? "#f0f0f0" : "#303030"
}`,
},
"& .MuiDataGrid-cell": {
color:
theme.palette.type === "light" ? "white" : "rgba(255,255,255,0.65)",
theme?.palette?.type === "light" ? "white" : "rgba(255,255,255,0.65)",
},
"& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption":
{
@@ -122,7 +124,8 @@ const useStyles = makeStyles((theme) => ({
color: "white",
},
},
}));
}
})
// Takes an action in Shuffle and
// Returns information about the icon, the color etc to be used
@@ -481,9 +484,9 @@ export const validateJson = (showResult) => {
};
}
} catch (e) {
showResult = showResult.split("'").join('"');
try {
showResult = showResult.split("'").join('"');
if (!showResult.includes("{") && !showResult.includes("[")) {
jsonvalid = false;
}
@@ -590,13 +593,37 @@ export const validateJson = (showResult) => {
};
};
//Custom hook for handling styling of the dropzone
const useDropzoneStyles = () => {
const { leftSideBarOpenByClick } = useContext(Context);
return {
maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
margin: "auto",
padding: 20,
paddingLeft: leftSideBarOpenByClick ? 200 : 0,
transition: "padding-left 0.3s ease",
};
};
//Wrapper for the dropzone component
const DropzoneWrapper = memo(({ onDrop, WorkflowView }) => {
const dropzoneStyles = useDropzoneStyles();
return (
<Dropzone style={dropzoneStyles} onDrop={onDrop}>
<WorkflowView />
</Dropzone>
);
});
const Workflows = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
document.title = "Shuffle - Workflows";
let navigate = useNavigate();
const classes = useStyles(theme);
const classes = useStyles(theme)
const imgSize = 60;
const referenceUrl = globalUrl + "/api/v1/hooks/";
@@ -1194,8 +1221,8 @@ const Workflows = (props) => {
return response.json();
})
.then((responseJson) => {
if (responseJson !== undefined) {
if (responseJson !== undefined) {
var newarray = []
for (var wfkey in responseJson) {
const wf = responseJson[wfkey]
@@ -1228,13 +1255,11 @@ const Workflows = (props) => {
}
}
if (newarray.length > 0) {
try {
localStorage.setItem("workflows", JSON.stringify(newarray))
} catch (e) {
console.log("Failed to set workflows in localstorage: ", e)
}
}
// Ensures the zooming happens only once per load
setTimeout(() => {
@@ -1285,20 +1310,10 @@ const Workflows = (props) => {
.catch((error) => {
toast(error.toString());
});
};
const handleKeysetting = (categorydata, workflows) => {
if (workflows !== undefined && workflows !== null) {
var newcategories = []
for (var key in categorydata) {
var category = categorydata[key]
// Check if category is bool
if (typeof category === "boolean") {
continue
}
const findMatches = (category, workflows) => {
category.matches = []
for (var subcategorykey in category.list) {
var subcategory = category.list[subcategorykey]
subcategory.matches = []
@@ -1328,6 +1343,20 @@ const Workflows = (props) => {
}
}
return category
}
const handleKeysetting = (categorydata, workflows) => {
if (workflows !== undefined && workflows !== null) {
var newcategories = []
for (var key in categorydata) {
var category = categorydata[key]
// Check if category is bool
if (typeof category === "boolean") {
continue
}
category = findMatches(category, workflows)
newcategories.push(category)
}
@@ -1440,7 +1469,7 @@ const Workflows = (props) => {
display: "flex",
boxSizing: "border-box",
position: "relative",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.surfaceColor,
};
@@ -2229,7 +2258,7 @@ const Workflows = (props) => {
}
return (
<div style={{width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "1px solid #40E0D0" : "inherit", borderRadius: theme.palette.borderRadius, }}>
<div style={{width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "1px solid #40E0D0" : "inherit", borderRadius: theme.palette?.borderRadius, }}>
<Paper square style={paperAppStyle}>
{selectedCategory !== "" ?
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
@@ -2277,7 +2306,7 @@ const Workflows = (props) => {
title={
<div style={{width: "100%", minWidth: 250, maxWidth: 310, }}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{backgroundColor: theme.palette.surfaceColor, maxWidth: 300, minWidth: 250, borderRadius: theme.palette.borderRadius, }} />
<img src={data.image} alt={data.name} style={{backgroundColor: theme.palette.surfaceColor, maxWidth: 300, minWidth: 250, borderRadius: theme.palette?.borderRadius, }} />
: null}
<Typography>
Edit '{data.name}'
@@ -2493,7 +2522,7 @@ const Workflows = (props) => {
{workflowMenuButtons}
</div>
) : null}
{(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data.input_markdown !== undefined && data.input_markdown !== null && data.input_markdown !== "") ?
{(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") ?
<Tooltip title="Edit Form" placement="top">
<div style={{position: "absolute", top: 45, right: 8, }}>
<IconButton
@@ -3061,9 +3090,6 @@ const Workflows = (props) => {
autoHeight
density="standard"
onSelectionModelChange={(newSelection) => {
//setSelectedWorkflows(newSelection.selectionModel);
console.log(newSelection)
setSelectedWorkflowIndexes(newSelection)
}}
selectionModel={selectedWorkflowIndexes}
@@ -3210,7 +3236,6 @@ const Workflows = (props) => {
<em>None</em>
</MenuItem>
{usecases.map((usecase, index) => {
//console.log(usecase)
return (
<span key={index}>
<ListSubheader
@@ -3586,7 +3611,7 @@ const Workflows = (props) => {
// );
// }
const WorkflowView = () => {
const WorkflowView = memo(() => {
if (workflows.length === 0) {
}
@@ -3660,9 +3685,14 @@ const Workflows = (props) => {
{!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ?
<div style={{ display: "flex", }}>
{usecases.map((usecase, index) => {
//console.log(usecase)
if (usecase.name === "5. Verify") {
return null
}
const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length/usecase.list.length*100) : 0
//console.log("Usecase Matches: ", usecase.matches, ", Percent: ", percentDone)
if (percentDone === 0) {
usecase = findMatches(usecase, workflows)
}
return (
<Paper
@@ -3671,7 +3701,7 @@ const Workflows = (props) => {
flex: 1,
backgroundImage: `linear-gradient(to right, ${usecase.color}, ${usecase.color} ${percentDone}%, transparent ${percentDone}%, transparent 100%)`,
backgroundColor: filters.includes(usecase.name.toLowerCase()) ? null : theme.palette.surfaceColor,
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
marginRight: index === usecases.length-1 ? 0 : 10,
cursor: "pointer",
border: `2px solid ${usecase.color}`,
@@ -3692,7 +3722,7 @@ const Workflows = (props) => {
<Typography variant="body1" color="textPrimary" style={{flex: 4, }}>
{usecase.name}
</Typography>
<Typography variant="body2" color="textSecondary" style={{flex: 1, marginTop: 5,}}>
<Typography variant="body2" color="textSecondary" style={{flex: 1, marginTop: 0,}}>
{usecase.matches.length}/{usecase.list.length}
</Typography>
</span>
@@ -3715,7 +3745,7 @@ const Workflows = (props) => {
minWidth: isMobile ? "100%" : 1024,
zIndex: 11,
border: "1px solid rgba(255,255,255,0.1)",
borderRadius: theme.palette.borderRadius,
borderRadius: theme.palette?.borderRadius,
textAlign: "center",
overflow: "auto",
}}
@@ -3734,7 +3764,6 @@ const Workflows = (props) => {
}
if (data.app_name.toLowerCase() === "integration framework") {
console.log("Skipping: ", data.app_name)
return null
}
@@ -3813,7 +3842,7 @@ const Workflows = (props) => {
) : null}
{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,
<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:
theme.palette.surfaceColor, display: "flex" , maxHeight: "105px", minHeight: "110px"}}
>
@@ -3905,7 +3934,7 @@ const Workflows = (props) => {
</div>
</div>
);
};
});
const importWorkflowsFromUrl = (url) => {
console.log("IMPORT WORKFLOWS FROM ", downloadUrl);
@@ -4173,7 +4202,7 @@ const Workflows = (props) => {
Setup progress: <b>{isNaN(percentDone) ? 0 : percentDone}%</b>
</Typography>
<LinearProgress color="primary" variant="determinate" value={percentDone} style={{marginTop: 5, height: 7, borderRadius: theme.palette.borderRadius, }} />
<LinearProgress color="primary" variant="determinate" value={percentDone} style={{marginTop: 5, height: 7, borderRadius: theme.palette?.borderRadius, }} />
<Typography variant="body2" style={{marginTop: 20, }}>
Follow these steps to get you up and running!
@@ -4251,17 +4280,7 @@ const Workflows = (props) => {
<TourButton />
</ShepherdTour>
*/}
<Dropzone
style={{
maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
margin: "auto",
padding: 20,
paddingLeft: userdata?.support ? 80 : 0
}}
onDrop={uploadFile}
>
<WorkflowView />
</Dropzone>
<DropzoneWrapper onDrop={uploadFile} WorkflowView={WorkflowView}/>
{/*modalView*/}
{deleteModal}
{exportVerifyModal}
@@ -4269,7 +4288,7 @@ const Workflows = (props) => {
{workflowDownloadModalOpen}
{/*!drawerOpen ?
<div style={{ position: "fixed", top: 64, right: -5, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, }}>
<div style={{ position: "fixed", top: 64, right: -5, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius, }}>
<Tooltip title={`Getting Started`} placement="bottom">
<IconButton onClick={() => {
setDrawerOpen(true)
-1
View File
@@ -782,7 +782,6 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
// image on every Orborus/new worker restart.
// Running as coroutine for eventual completeness
//go shuffle.DownloadDockerImageBackend(&http.Client{}, image)
// FIXME: With goroutines it got too much trouble of deploying with an older version
// Allowing slow startups, as long as it's eventually fast, and uses the same registry as on host.
shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
-83
View File
@@ -1,83 +0,0 @@
#!/bin/bash
# Update and install dependencies
apt-get update
apt-get install -y docker.io docker-compose curl git
# Start and enable Docker
systemctl start docker
systemctl enable docker
# Clone Shuffle repository
git clone --branch 2.0.0 https://github.com/Shuffle/Shuffle.git
cd Shuffle
# Setup directories and permissions
mkdir shuffle-database && chmod -R 777 shuffle-database
# Start services
docker-compose up -d
# Wait for initial startup
echo "Waiting 30 seconds for initial startup..."
sleep 30
# Check for restarting containers
echo "Checking for restarting containers..."
ATTEMPTS=30
for i in $(seq 1 $ATTEMPTS); do
RESTARTING_CONTAINERS=$(docker ps --filter "status=restarting" --format "{{.Names}}")
if [ -n "$RESTARTING_CONTAINERS" ]; then
echo "The following containers are restarting:"
echo "$RESTARTING_CONTAINERS"
exit 1
fi
echo "No containers are restarting. Attempt $i/$ATTEMPTS."
sleep 1
done
echo "No containers were found in a restarting state after $ATTEMPTS checks."
# Check frontend response
echo "Checking frontend response..."
RESPONSE=$(curl -s http://localhost:3001)
if echo "$RESPONSE" | grep -q "Shuffle"; then
echo "The word 'Shuffle' was found in the response."
else
echo "The word 'Shuffle' was not found in the response."
exit 1
fi
# Register user
echo "Attempting to register user..."
MAX_RETRIES=30
RETRY_INTERVAL=10
CONTAINER_NAME="shuffle-backend"
for (( i=1; i<=$MAX_RETRIES; i++ ))
do
STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" 'http://localhost:3001/api/v1/register' \
-H 'Accept: */*' \
-H 'Accept-Language: en-US,en;q=0.9' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json' \
--data-raw '{"username":"demo@demo.io","password":"supercoolpassword"}')
if [ "$STATUS_CODE" -eq 200 ]; then
echo "User registration was successful with status code 200."
exit 0
elif [ "$STATUS_CODE" -ne 502 ]; then
echo "User registration failed with status code $STATUS_CODE."
exit 1
fi
echo "Received status code $STATUS_CODE. Retrying in $RETRY_INTERVAL seconds... ($i/$MAX_RETRIES)"
echo "Fetching last 30 lines of logs from container $CONTAINER_NAME..."
docker logs --tail 30 "$CONTAINER_NAME"
echo "Fetching last 30 lines of logs from container shuffle-opensearch..."
docker logs --tail 30 shuffle-opensearch
sleep $RETRY_INTERVAL
done
echo "User registration failed after $MAX_RETRIES attempts."
exit 1