diff --git a/.env b/.env old mode 100644 new mode 100755 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md old mode 100644 new mode 100755 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md old mode 100644 new mode 100755 diff --git a/.github/install-aws.md b/.github/install-aws.md new file mode 100644 index 00000000..35bc140b --- /dev/null +++ b/.github/install-aws.md @@ -0,0 +1,38 @@ +## Install Shuffle on AWS + +First, you need to create your own VPC for the same range of private IP addresses. + +**To create a VPC in AWS, follow these steps** + +1. Sign in to the AWS Management Console and open the Amazon VPC console at https://console.aws.amazon.com/vpc/. +2. In the top navigation bar, choose the region in which you want to create the VPC. +3. In the navigation pane, choose Your VPCs & Choose Create VPC. +4. Enter a name for your VPC in the Name tag field & Choose VPC and more option. +5. Specify the IPv4 CIDR block for your VPC. The CIDR block is the range of IP addresses that will be available for use within your VPC. You can specify any CIDR block that is: + - Between a /16 and /28 netmask (inclusive) + - Not currently in use +6. Specify the AZs, Number of public subnets & Number of private subnets. + +![image](https://user-images.githubusercontent.com/118437260/211500830-30c52dc0-0688-47f9-8ee7-eb7f9b31a9b8.png) + +7. Choose Yes, Create VPC. + +Your VPC will be created and will appear in the list of Your VPCs. By default, a VPC includes a default security group and a default network ACL. You can customize your VPC by adding subnets, security groups, network ACLs, and other resources. + + + +**To create an EC2 instance in AWS, follow these steps:** + +1. Sign in to the AWS Management Console and open the Amazon EC2 console at https://console.aws.amazon.com/ec2/. +2. In the top navigation bar, choose the region in which you want to create the instance. +3. In the navigation pane, choose Instances and Choose Launch Instance. +4. On the Choose an Amazon Machine Image (AMI) page, choose an AMI. An AMI is a template that contains the software configuration (operating system, application server, and applications) for your instance. +5. On the Choose an Instance Type page, choose the hardware configuration of your instance. +6. On the Select an existing key pair or create a new key pair dialog box, choose an existing key pair or create a new one. +7. On the network settings page and click on edit and select your VPC & subnet. +8. On the Configure Security Group page, configure the security group for your instance. A security group acts as a virtual firewall for your instance to control inbound and outbound traffic. + +![image](https://user-images.githubusercontent.com/118437260/211514598-1c95e459-b98a-4579-b3a7-a92bf36e9f50.png) + +9. On the Add Storage page, add storage to your instance. +10. Review your instance launch details and choose Launch. diff --git a/.github/install-guide.html b/.github/install-guide.html old mode 100644 new mode 100755 diff --git a/.github/install-guide.md b/.github/install-guide.md old mode 100644 new mode 100755 index 53e39401..48b3689e --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -1,7 +1,7 @@ # Installation guide Installation of Shuffle is currently only available in docker. Looking for how to update Shuffle? Check the [updating guide](https://shuffler.io/docs/configuration#updating_shuffle) -This document outlines a an introduction environment which is not scalable. [Read here](https://shuffler.io/docs/configuration#production_readiness) for information on production readiness. This also includes system requirements and configurations for Swarm or K8s. +This document outlines a an introduction environment which is not scalable. [Read here](https://shuffler.io/docs/configuration#production_readiness) for information on production readiness. This also includes system requirements and configurations for Swarm or Kubernetes. # Docker - *nix The Docker setup is done with docker-compose @@ -19,6 +19,7 @@ cd Shuffle ```bash mkdir shuffle-database sudo chown -R 1000:1000 shuffle-database +# IF you get an error using 'chown', add the user first with 'sudo useradd opensearch' ``` 4. Run docker-compose. @@ -26,7 +27,12 @@ sudo chown -R 1000:1000 shuffle-database docker-compose up -d ``` -When you're done, skip to the "After installation" step below. +5. Recommended for Opensearch to work well +```bash +sudo sysctl -w vm.max_map_count=262144 # https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html +``` + +When you're done, skip to the [After installation](#after-installation) step below. ## Windows with WSL This step is for setting up with Docker on windows from scratch. @@ -57,7 +63,7 @@ https://shuffler.io/docs/configuration 3. Sign in with the same Username & Password! Go to /apps and see if you have any apps yet. If not - you may need to [configure proxies](https://shuffler.io/docs/configuration#production_readiness) 4. Check out https://shuffler.io/docs/configuration as it has a lot of useful information to get started -![Admin account setup](https://github.com/frikky/Shuffle/blob/master/frontend/src/assets/img/shuffle_adminaccount.png) +![Admin account setup](https://github.com/Shuffle/Shuffle/blob/main/frontend/src/assets/img/shuffle_adminaccount.png?raw=true) ### Useful info * Check out [getting started](https://shuffler.io/docs/getting_started) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml deleted file mode 100644 index 716b42db..00000000 --- a/.github/workflows/docker-build.yaml +++ /dev/null @@ -1,66 +0,0 @@ -name: docker-build - -on: - push: - branches: launch -jobs: - main: - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental }} - strategy: - fail-fast: false - matrix: - include: - - app: frontend - path: frontend - version: 1.0.0 - experimental: true - - app: backend - path: backend - version: 1.0.0 - experimental: false - - app: orborus - path: functions/onprem/orborus - version: 1.0.0 - experimental: false - - app: database - path: backend/database - version: 1.0.0 - experimental: false - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} -# Use below configuration for ghcr.io -# with: -# registry: ghcr.io -# username: ${{ github.repository_owner }} -# password: ${{ secrets.CR_PAT }} - - - name: Build and push - id: docker_build - uses: docker/build-push-action@v2 - env: - BUILDX_NO_DEFAULT_LOAD: true - with: - context: ${{ matrix.path }}/ - file: ${{ matrix.path }}/Dockerfile - platforms: linux/amd64,linux/arm64 - #,linux/386 - no node image I guess? - push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.app }}:${{ matrix.version }} - - - name: Image digest - run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 0594f24b..bdc7b0d6 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -2,7 +2,7 @@ name: dockerbuild on: push: - branches: 1.2.0 + branches: [main, 1.3.0] jobs: main: runs-on: ubuntu-latest @@ -35,12 +35,14 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: 'amd64,arm64,arm' + - name: Login to DockerHub uses: docker/login-action@v2 with: diff --git a/.github/workflows/project_automation.yml b/.github/workflows/project_automation.yml new file mode 100644 index 00000000..0e3bf945 --- /dev/null +++ b/.github/workflows/project_automation.yml @@ -0,0 +1,16 @@ +name: Automation - Add all new issues to roadmap project + +on: + issues: + types: + - opened + +jobs: + add-to-project: + name: Add issue to project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/Shuffle/projects/8 + github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} diff --git a/.github/workflows/snyk-container-analysis.yml b/.github/workflows/snyk-container-analysis.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/snyk-infrastructure-analysis.yml b/.github/workflows/snyk-infrastructure-analysis.yml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index e937f939..797dd530 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Follow us on Twitter at [@shuffleio](https://twitter.com/shuffleio). -![Example Shuffle webhook integration](https://github.com/frikky/Shuffle/blob/master/frontend/src/assets/img/github_shuffle_img.png) +![Example Shuffle webhook integration](https://github.com/frikky/Shuffle/blob/main/frontend/src/assets/img/github_shuffle_img.png) ## Try it * Self-hosted: Check out the [installation guide](https://github.com/frikky/shuffle/blob/master/.github/install-guide.md) @@ -96,7 +96,7 @@ Shuffle backend: AGPLv3 Apps, specification and App SDK: MIT ## Architecture -![Shuffle Architecture](https://github.com/frikky/Shuffle/blob/master/frontend/src/assets/img/shuffle_architecture.png) +![Shuffle Architecture](https://github.com/frikky/Shuffle/blob/main/frontend/src/assets/img/shuffle_architecture.png) ### Repository overview Below is the folder structure with a short explanation diff --git a/SECURITY.md b/SECURITY.md old mode 100644 new mode 100755 diff --git a/backend/Dockerfile b/backend/Dockerfile old mode 100644 new mode 100755 diff --git a/backend/README.md b/backend/README.md old mode 100644 new mode 100755 diff --git a/backend/app_gen/LICENSE b/backend/app_gen/LICENSE old mode 100644 new mode 100755 diff --git a/backend/app_gen/openapi-parsers/misp.py b/backend/app_gen/openapi-parsers/misp.py old mode 100644 new mode 100755 diff --git a/backend/app_gen/openapi-parsers/swimlane.py b/backend/app_gen/openapi-parsers/swimlane.py old mode 100644 new mode 100755 diff --git a/backend/app_gen/openapi/README.md b/backend/app_gen/openapi/README.md old mode 100644 new mode 100755 diff --git a/backend/app_gen/openapi/baseline/Dockerfile b/backend/app_gen/openapi/baseline/Dockerfile old mode 100644 new mode 100755 diff --git a/backend/app_gen/openapi/baseline/requirements.txt b/backend/app_gen/openapi/baseline/requirements.txt old mode 100644 new mode 100755 diff --git a/backend/app_gen/openapi/test.go b/backend/app_gen/openapi/test.go old mode 100644 new mode 100755 diff --git a/backend/app_gen/openapi/testGCP.go b/backend/app_gen/openapi/testGCP.go old mode 100644 new mode 100755 diff --git a/backend/app_gen/python-lib/README.md b/backend/app_gen/python-lib/README.md old mode 100644 new mode 100755 diff --git a/backend/app_gen/python-lib/baseline/Dockerfile b/backend/app_gen/python-lib/baseline/Dockerfile old mode 100644 new mode 100755 diff --git a/backend/app_gen/python-lib/baseline/docker-compose.yml b/backend/app_gen/python-lib/baseline/docker-compose.yml old mode 100644 new mode 100755 diff --git a/backend/app_gen/python-lib/baseline/env.txt b/backend/app_gen/python-lib/baseline/env.txt old mode 100644 new mode 100755 diff --git a/backend/app_gen/python-lib/baseline/requirements.txt b/backend/app_gen/python-lib/baseline/requirements.txt old mode 100644 new mode 100755 diff --git a/backend/app_gen/python-lib/generator.py b/backend/app_gen/python-lib/generator.py old mode 100644 new mode 100755 diff --git a/backend/app_gen/python-lib/requirements.txt b/backend/app_gen/python-lib/requirements.txt old mode 100644 new mode 100755 diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile old mode 100644 new mode 100755 diff --git a/backend/app_sdk/Dockerfile_blackarch b/backend/app_sdk/Dockerfile_blackarch old mode 100644 new mode 100755 diff --git a/backend/app_sdk/Dockerfile_kali b/backend/app_sdk/Dockerfile_kali old mode 100644 new mode 100755 diff --git a/backend/app_sdk/LICENSE b/backend/app_sdk/LICENSE old mode 100644 new mode 100755 diff --git a/backend/app_sdk/README.md b/backend/app_sdk/README.md old mode 100644 new mode 100755 diff --git a/backend/app_sdk/__init__.py b/backend/app_sdk/__init__.py old mode 100644 new mode 100755 diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py old mode 100644 new mode 100755 diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh old mode 100644 new mode 100755 diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt old mode 100644 new mode 100755 diff --git a/backend/build.sh b/backend/build.sh old mode 100644 new mode 100755 diff --git a/backend/database/opensearch/docker-compose.yml b/backend/database/opensearch/docker-compose.yml old mode 100644 new mode 100755 diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go old mode 100644 new mode 100755 index 8cb07ff6..a6324faf --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -534,7 +534,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { if len(img.ID) == 0 { if len(img2.ID) == 0 { workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 0, 0) - //log.Printf("[INFO] Getting workflowapps for a rebuild. Got %d with err %#v", len(workflowapps), err) + log.Printf("[INFO] Getting workflowapps for a rebuild. Got %d with err %#v", len(workflowapps), err) if err == nil { imageName := "" imageVersion := "" diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod old mode 100644 new mode 100755 index 7a0e631e..b43db79b --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -49,7 +49,6 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/frikky/go-elasticsearch/v8 v8.13.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.19.5 // indirect @@ -102,4 +101,5 @@ require ( google.golang.org/protobuf v1.30.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + ) diff --git a/backend/go-app/main.go b/backend/go-app/main.go old mode 100644 new mode 100755 index 3782d239..b1304b4f --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5997,6 +5997,7 @@ func initHandlers() { // New for recommendations in Shuffle r.HandleFunc("/api/v1/recommendations/get_actions", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/recommendations/modify", shuffle.HandleRecommendationAction).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/revisions", shuffle.GetWorkflowRevisions).Methods("GET", "OPTIONS") // Triggers r.HandleFunc("/api/v1/hooks/new", shuffle.HandleNewHook).Methods("POST", "OPTIONS") diff --git a/backend/go-app/main_test.go b/backend/go-app/main_test.go old mode 100644 new mode 100755 diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go old mode 100644 new mode 100755 index 631c653d..75c979dd --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -636,6 +636,7 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { } //log.Printf("Actionresult unmarshal: %s", string(body)) + log.Printf("[DEBUG] Got workflow result from %s of length %d", request.RemoteAddr, len(body)) ctx := context.Background() err = shuffle.ValidateNewWorkerExecution(ctx, body) if err == nil { @@ -774,7 +775,6 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl } //log.Printf("BASE LENGTH: %d", len(workflowExecution.Results)) - workflowExecution, dbSave, err := shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, false, 0) if err != nil { b, suberr := json.Marshal(actionResult) @@ -1118,6 +1118,680 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request err = imageCheckBuilder(execInfo.ImageNames) if err != nil { log.Printf("[ERROR] Failed building the required images from %#v: %s", execInfo.ImageNames, err) + return shuffle.WorkflowExecution{}, "Failed unmarshal during execution", err + } + + makeNew := true + start, startok := request.URL.Query()["start"] + if request.Method == "POST" { + body, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("[ERROR] Failed request POST read: %s", err) + return shuffle.WorkflowExecution{}, "Failed getting body", err + } + + // This one doesn't really matter. + log.Printf("[INFO] Running POST execution with body of length %d for workflow %s", len(string(body)), workflowExecution.Workflow.ID) + + if len(body) >= 4 { + if body[0] == 34 && body[len(body)-1] == 34 { + body = body[1 : len(body)-1] + } + if body[0] == 34 && body[len(body)-1] == 34 { + body = body[1 : len(body)-1] + } + } + + sourceAuth, sourceAuthOk := request.URL.Query()["source_auth"] + if sourceAuthOk { + //log.Printf("\n\n\nSETTING SOURCE WORKFLOW AUTH TO %s!!!\n\n\n", sourceAuth[0]) + workflowExecution.ExecutionSourceAuth = sourceAuth[0] + } else { + //log.Printf("Did NOT get source workflow") + } + + sourceNode, sourceNodeOk := request.URL.Query()["source_node"] + if sourceNodeOk { + //log.Printf("\n\n\nSETTING SOURCE WORKFLOW NODE TO %s!!!\n\n\n", sourceNode[0]) + workflowExecution.ExecutionSourceNode = sourceNode[0] + } else { + //log.Printf("Did NOT get source workflow") + } + + //workflowExecution.ExecutionSource = "default" + sourceWorkflow, sourceWorkflowOk := request.URL.Query()["source_workflow"] + if sourceWorkflowOk { + //log.Printf("Got source workflow %s", sourceWorkflow) + workflowExecution.ExecutionSource = sourceWorkflow[0] + } else { + //log.Printf("Did NOT get source workflow") + } + + sourceExecution, sourceExecutionOk := request.URL.Query()["source_execution"] + if sourceExecutionOk { + //log.Printf("[INFO] Got source execution%s", sourceExecution) + workflowExecution.ExecutionParent = sourceExecution[0] + } else { + //log.Printf("Did NOT get source execution") + } + + if len(string(body)) < 50 { + //log.Println(body) + // String in string + //log.Println(body) + + //if string(body)[0] == "\"" && string(body)[string(body) + log.Printf("[DEBUG] Body: %s", string(body)) + } + + var execution shuffle.ExecutionRequest + err = json.Unmarshal(body, &execution) + if err != nil { + log.Printf("[WARNING] Failed execution POST unmarshaling - continuing anyway: %s", err) + //return shuffle.WorkflowExecution{}, "", err + } + + if execution.Start == "" && len(body) > 0 { + execution.ExecutionArgument = string(body) + } + + // FIXME - this should have "execution_argument" from executeWorkflow frontend + //log.Printf("EXEC: %#v", execution) + if len(execution.ExecutionArgument) > 0 { + workflowExecution.ExecutionArgument = execution.ExecutionArgument + } + + if len(execution.ExecutionSource) > 0 { + workflowExecution.ExecutionSource = execution.ExecutionSource + } + + //log.Printf("Execution data: %#v", execution) + if len(execution.Start) == 36 && len(workflow.Actions) > 0 { + log.Printf("[INFO] Should start execution on node %s", execution.Start) + workflowExecution.Start = execution.Start + + found := false + for _, action := range workflow.Actions { + if action.ID == execution.Start { + found = true + break + } + } + + if !found { + log.Printf("[ERROR] Action %s was NOT found! Exiting execution.", execution.Start) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Startnode %s was not found in actions", workflow.Start), errors.New(fmt.Sprintf("Startnode %s was not found in actions", workflow.Start)) + } + } else if len(execution.Start) > 0 { + //log.Printf("[INFO] !") + //log.Printf("[ERROR] START ACTION %s IS WRONG ID LENGTH %d!", execution.Start, len(execution.Start)) + //return shuffle.WorkflowExecution{}, fmt.Sprintf("Startnode %s was not found in actions", execution.Start), errors.New(fmt.Sprintf("Startnode %s was not found in actions", execution.Start)) + } + + if len(execution.ExecutionId) == 36 { + workflowExecution.ExecutionId = execution.ExecutionId + } else { + sessionToken := uuid.NewV4() + workflowExecution.ExecutionId = sessionToken.String() + } + } else { + // Check for parameters of start and ExecutionId + // This is mostly used for user input trigger + + answer, answerok := request.URL.Query()["answer"] + referenceId, referenceok := request.URL.Query()["reference_execution"] + if answerok && referenceok { + // If answer is false, reference execution with result + log.Printf("[INFO] Answer is OK AND reference is OK!") + if answer[0] == "false" { + log.Printf("Should update reference and return, no need for further execution!") + + // Get the reference execution + oldExecution, err := shuffle.GetWorkflowExecution(ctx, referenceId[0]) + if err != nil { + log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err + } + + if oldExecution.Workflow.ID != id { + log.Println("Wrong workflowid!") + return shuffle.WorkflowExecution{}, fmt.Sprintf("Bad ID %s", referenceId), errors.New("Bad ID") + } + + newResults := []shuffle.ActionResult{} + //log.Printf("%#v", oldExecution.Results) + for _, result := range oldExecution.Results { + log.Printf("%s - %s", result.Action.ID, start[0]) + if result.Action.ID == start[0] { + note, noteok := request.URL.Query()["note"] + if noteok { + result.Result = fmt.Sprintf("User note: %s", note[0]) + } else { + result.Result = fmt.Sprintf("User clicked %s", answer[0]) + } + + // Stopping the whole thing + result.CompletedAt = int64(time.Now().Unix()) + result.Status = "ABORTED" + oldExecution.Status = result.Status + oldExecution.Result = result.Result + oldExecution.LastNode = result.Action.ID + } + + newResults = append(newResults, result) + } + + oldExecution.Results = newResults + err = shuffle.SetWorkflowExecution(ctx, *oldExecution, true) + if err != nil { + log.Printf("Error saving workflow execution actionresult setting: %s", err) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed setting workflowexecution actionresult in execution: %s", err), err + } + + return shuffle.WorkflowExecution{}, "", nil + } + } + + if referenceok { + log.Printf("Handling an old execution continuation!") + // Will use the old name, but still continue with NEW ID + oldExecution, err := shuffle.GetWorkflowExecution(ctx, referenceId[0]) + if err != nil { + log.Printf("Failed getting execution (execution) %s: %s", referenceId[0], err) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed getting execution ID %s because it doesn't exist.", referenceId[0]), err + } + + workflowExecution = *oldExecution + } + + if len(workflowExecution.ExecutionId) == 0 { + sessionToken := uuid.NewV4() + workflowExecution.ExecutionId = sessionToken.String() + } else { + log.Printf("Using the same executionId as before: %s", workflowExecution.ExecutionId) + makeNew = false + } + + // Don't override workflow defaults + } + + if startok { + //log.Printf("\n\n[INFO] Setting start to %s based on query!\n\n", start[0]) + //workflowExecution.Workflow.Start = start[0] + workflowExecution.Start = start[0] + } + + // FIXME - regex uuid, and check if already exists? + if len(workflowExecution.ExecutionId) != 36 { + log.Printf("Invalid uuid: %s", workflowExecution.ExecutionId) + return shuffle.WorkflowExecution{}, "Invalid uuid", err + } + + // FIXME - find owner of workflow + // FIXME - get the actual workflow itself and build the request + // MAYBE: Don't send the workflow within the pubsub, as this requires more data to be sent + // Check if a worker already exists for company, else run one with: + // locations, project IDs and subscription names + + // When app is executed: + // Should update with status execution (somewhere), which will trigger the next node + // IF action.type == internal, we need the internal watcher to be running and executing + // This essentially means the WORKER has to be the responsible party for new actions in the INTERNAL landscape + // Results are ALWAYS posted back to cloud@execution_id? + if makeNew { + workflowExecution.Type = "workflow" + //workflowExecution.Stream = "tmp" + //workflowExecution.WorkflowQueue = "tmp" + //workflowExecution.SubscriptionNameNodestream = "testcompany-nodestream" + //workflowExecution.Locations = []string{"europe-west2"} + workflowExecution.ProjectId = gceProject + workflowExecution.WorkflowId = workflow.ID + workflowExecution.StartedAt = int64(time.Now().Unix()) + workflowExecution.CompletedAt = 0 + workflowExecution.Authorization = uuid.NewV4().String() + + // Status for the entire workflow. + workflowExecution.Status = "EXECUTING" + } + + if len(workflowExecution.ExecutionSource) == 0 { + log.Printf("[INFO] No execution source (trigger) specified. Setting to default") + workflowExecution.ExecutionSource = "default" + } else { + log.Printf("[INFO] Execution source is %s for execution ID %s in workflow %s", workflowExecution.ExecutionSource, workflowExecution.ExecutionId, workflowExecution.Workflow.ID) + } + + workflowExecution.ExecutionVariables = workflow.ExecutionVariables + if len(workflowExecution.Start) == 0 && len(workflowExecution.Workflow.Start) > 0 { + workflowExecution.Start = workflowExecution.Workflow.Start + } + + startnodeFound := false + newStartnode := "" + for _, item := range workflowExecution.Workflow.Actions { + if item.ID == workflowExecution.Start { + startnodeFound = true + } + + if item.IsStartNode { + newStartnode = item.ID + } + } + + if !startnodeFound { + log.Printf("[INFO] Couldn't find startnode %s. Remapping to %#v", workflowExecution.Start, newStartnode) + + if len(newStartnode) > 0 { + workflowExecution.Start = newStartnode + } else { + return shuffle.WorkflowExecution{}, fmt.Sprintf("Startnode couldn't be found"), errors.New("Startnode isn't defined in this workflow..") + } + } + + childNodes := shuffle.FindChildNodes(workflowExecution, workflowExecution.Start, []string{}, []string{}) + + //topic := "workflows" + startFound := false + // FIXME - remove this? + newActions := []shuffle.Action{} + defaultResults := []shuffle.ActionResult{} + + allAuths := []shuffle.AppAuthenticationStorage{} + for _, action := range workflowExecution.Workflow.Actions { + //action.LargeImage = "" + if action.ID == workflowExecution.Start { + startFound = true + } + //log.Println(action.Environment) + + if action.Environment == "" { + return shuffle.WorkflowExecution{}, fmt.Sprintf("Environment is not defined for %s", action.Name), errors.New("Environment not defined!") + } + + // FIXME: Authentication parameters + if len(action.AuthenticationId) > 0 { + if len(allAuths) == 0 { + allAuths, err = shuffle.GetAllWorkflowAppAuth(ctx, workflow.ExecutingOrg.Id) + if err != nil { + log.Printf("Api authentication failed in get all app auth: %s", err) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Api authentication failed in get all app auth: %s", err), err + } + } + + curAuth := shuffle.AppAuthenticationStorage{Id: ""} + for _, auth := range allAuths { + if auth.Id == action.AuthenticationId { + curAuth = auth + break + } + } + + if len(curAuth.Id) == 0 { + return shuffle.WorkflowExecution{}, fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId), errors.New(fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId)) + } + + if curAuth.Encrypted { + setField := true + newFields := []shuffle.AuthenticationStore{} + for _, field := range curAuth.Fields { + parsedKey := fmt.Sprintf("%s_%d_%s_%s", curAuth.OrgId, curAuth.Created, curAuth.Label, field.Key) + newValue, err := shuffle.HandleKeyDecryption([]byte(field.Value), parsedKey) + if err != nil { + log.Printf("[WARNING] Failed decryption for %s: %s", field.Key, err) + setField = false + break + } + + field.Value = string(newValue) + newFields = append(newFields, field) + } + + if setField { + curAuth.Fields = newFields + } + } else { + log.Printf("[INFO] AUTH IS NOT ENCRYPTED - attempting encrypting!") + err = shuffle.SetWorkflowAppAuthDatastore(ctx, curAuth, curAuth.Id) + if err != nil { + log.Printf("[WARNING] Failed running encryption during execution: %s", err) + } + } + + newParams := []shuffle.WorkflowAppActionParameter{} + if strings.ToLower(curAuth.Type) == "oauth2" { + log.Printf("[DEBUG] Should replace auth parameters (Oauth2)") + + for _, param := range curAuth.Fields { + if param.Key == "expiration" { + continue + } + + newParams = append(newParams, shuffle.WorkflowAppActionParameter{ + Name: param.Key, + Value: param.Value, + }) + } + + for _, param := range action.Parameters { + //log.Printf("Param: %#v", param) + if param.Configuration { + continue + } + + newParams = append(newParams, param) + } + } else { + // Rebuild params with the right data. This is to prevent issues on the frontend + for _, param := range action.Parameters { + + for _, authparam := range curAuth.Fields { + if param.Name == authparam.Key { + param.Value = authparam.Value + //log.Printf("Name: %s - value: %s", param.Name, param.Value) + //log.Printf("Name: %s - value: %s\n", param.Name, param.Value) + break + } + } + + newParams = append(newParams, param) + } + } + + action.Parameters = newParams + } + + action.LargeImage = "" + if len(action.Label) == 0 { + action.Label = action.ID + } + //log.Printf("LABEL: %s", action.Label) + newActions = append(newActions, action) + + // If the node is NOT found, it's supposed to be set to SKIPPED, + // as it's not a childnode of the startnode + // This is a configuration item for the workflow itself. + if len(workflowExecution.Results) > 0 { + defaultResults = []shuffle.ActionResult{} + for _, result := range workflowExecution.Results { + if result.Status == "WAITING" { + result.Status = "FINISHED" + result.Result = "Continuing" + } + + defaultResults = append(defaultResults, result) + } + } else if len(workflowExecution.Results) == 0 && !workflowExecution.Workflow.Configuration.StartFromTop { + found := false + for _, nodeId := range childNodes { + if nodeId == action.ID { + //log.Printf("Found %s", action.ID) + found = true + } + } + + if !found { + if action.ID == workflowExecution.Start { + continue + } + + //log.Printf("[WARNING] Set %s to SKIPPED as it's NOT a childnode of the startnode.", action.ID) + curaction := shuffle.Action{ + AppName: action.AppName, + AppVersion: action.AppVersion, + Label: action.Label, + Name: action.Name, + ID: action.ID, + } + //action + //curaction.Parameters = [] + defaultResults = append(defaultResults, shuffle.ActionResult{ + Action: curaction, + ExecutionId: workflowExecution.ExecutionId, + Authorization: workflowExecution.Authorization, + Result: "Skipped because it's not under the startnode", + StartedAt: 0, + CompletedAt: 0, + Status: "SKIPPED", + }) + } + } + } + + removeTriggers := []string{} + for triggerIndex, trigger := range workflowExecution.Workflow.Triggers { + //log.Printf("[INFO] ID: %s vs %s", trigger.ID, workflowExecution.Start) + if trigger.ID == workflowExecution.Start { + if trigger.AppName == "User Input" { + startFound = true + break + } + } + + if trigger.AppName == "User Input" || trigger.AppName == "Shuffle Workflow" { + found := false + for _, node := range childNodes { + if node == trigger.ID { + found = true + break + } + } + + if !found { + //log.Printf("SHOULD SET TRIGGER %s TO BE SKIPPED", trigger.ID) + + curaction := shuffle.Action{ + AppName: "shuffle-subflow", + AppVersion: trigger.AppVersion, + Label: trigger.Label, + Name: trigger.Name, + ID: trigger.ID, + } + + defaultResults = append(defaultResults, shuffle.ActionResult{ + Action: curaction, + ExecutionId: workflowExecution.ExecutionId, + Authorization: workflowExecution.Authorization, + Result: "Skipped because it's not under the startnode", + StartedAt: 0, + CompletedAt: 0, + Status: "SKIPPED", + }) + } else { + // Replaces trigger with the subflow + //if trigger.AppName == "Shuffle Workflow" { + // replaceActions := false + // workflowAction := "" + // for _, param := range trigger.Parameters { + // if param.Name == "argument" && !strings.Contains(param.Value, ".#") { + // replaceActions = true + // } + + // if param.Name == "startnode" { + // workflowAction = param.Value + // } + // } + + // if replaceActions { + // replacementNodes, newBranches, lastnode := shuffle.GetReplacementNodes(ctx, workflowExecution, trigger, trigger.Label) + // log.Printf("REPLACEMENTS: %d, %d", len(replacementNodes), len(newBranches)) + // if len(replacementNodes) > 0 { + // for _, action := range replacementNodes { + // found := false + + // for subActionIndex, subaction := range newActions { + // if subaction.ID == action.ID { + // found = true + // //newActions[subActionIndex].Name = action.Name + // newActions[subActionIndex].Label = action.Label + // break + // } + // } + + // if !found { + // action.SubAction = true + // newActions = append(newActions, action) + // } + + // // Check if it's already set to have a value + // for resultIndex, result := range defaultResults { + // if result.Action.ID == action.ID { + // defaultResults = append(defaultResults[:resultIndex], defaultResults[resultIndex+1:]...) + // break + // } + // } + // } + + // for _, branch := range newBranches { + // workflowExecution.Workflow.Branches = append(workflowExecution.Workflow.Branches, branch) + // } + + // // Append branches: + // // parent -> new inner node (FIRST one) + // for branchIndex, branch := range workflowExecution.Workflow.Branches { + // if branch.DestinationID == trigger.ID { + // log.Printf("REPLACE DESTINATION WITH %s!!", workflowAction) + // workflowExecution.Workflow.Branches[branchIndex].DestinationID = workflowAction + // } + + // if branch.SourceID == trigger.ID { + // log.Printf("REPLACE SOURCE WITH LASTNODE %s!!", lastnode) + // workflowExecution.Workflow.Branches[branchIndex].SourceID = lastnode + // } + // } + + // // Remove the trigger + // removeTriggers = append(removeTriggers, workflowExecution.Workflow.Triggers[triggerIndex].ID) + // } + + // log.Printf("NEW ACTION LENGTH %d, RESULT: %d, Triggers: %d, BRANCHES: %d", len(newActions), len(defaultResults), len(workflowExecution.Workflow.Triggers), len(workflowExecution.Workflow.Branches)) + // } + //} + _ = triggerIndex + } + } + } + + //newTriggers := []shuffle.Trigger{} + //for _, trigger := range workflowExecution.Workflow.Triggers { + // found := false + // for _, triggerId := range removeTriggers { + // if trigger.ID == triggerId { + // found = true + // break + // } + // } + + // if found { + // log.Printf("[WARNING] Removed trigger %s during execution", trigger.ID) + // continue + // } + + // newTriggers = append(newTriggers, trigger) + //} + //workflowExecution.Workflow.Triggers = newTriggers + _ = removeTriggers + + if !startFound { + if len(workflowExecution.Start) == 0 && len(workflowExecution.Workflow.Start) > 0 { + workflowExecution.Start = workflow.Start + } else if len(workflowExecution.Workflow.Actions) > 0 { + workflowExecution.Start = workflowExecution.Workflow.Actions[0].ID + } else { + log.Printf("[ERROR] Startnode %s doesn't exist!!", workflowExecution.Start) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Workflow action %s doesn't exist in workflow", workflowExecution.Start), errors.New(fmt.Sprintf(`Workflow start node "%s" doesn't exist. Exiting!`, workflowExecution.Start)) + } + } + + //log.Printf("EXECUTION START: %s", workflowExecution.Start) + + // Verification for execution environments + workflowExecution.Results = defaultResults + workflowExecution.Workflow.Actions = newActions + onpremExecution := true + _ = onpremExecution + environments := []string{} + + if len(workflowExecution.ExecutionOrg) == 0 && len(workflow.ExecutingOrg.Id) > 0 { + workflowExecution.ExecutionOrg = workflow.ExecutingOrg.Id + } + + var allEnvs []shuffle.Environment + if len(workflowExecution.ExecutionOrg) > 0 { + //log.Printf("[INFO] Executing ORG: %s", workflowExecution.ExecutionOrg) + + allEnvironments, err := shuffle.GetEnvironments(ctx, workflowExecution.ExecutionOrg) + if err != nil { + log.Printf("[WARNING] Failed finding environments: %s", err) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Workflow environments not found for this org"), errors.New(fmt.Sprintf("Workflow environments not found for this org")) + } + + for _, curenv := range allEnvironments { + if curenv.Archived { + continue + } + + allEnvs = append(allEnvs, curenv) + } + } else { + log.Printf("[ERROR] No org identified for execution of %s. Returning", workflowExecution.Workflow.ID) + return shuffle.WorkflowExecution{}, "No org identified for execution", errors.New("No org identified for execution") + } + + if len(allEnvs) == 0 { + log.Printf("[ERROR] No active environments found for org: %s", workflowExecution.ExecutionOrg) + return shuffle.WorkflowExecution{}, "No active environments found", errors.New(fmt.Sprintf("No active env found for org %s", workflowExecution.ExecutionOrg)) + } + + // Check if the actions are children of the startnode? + imageNames := []string{} + cloudExec := false + _ = cloudExec + for _, action := range workflowExecution.Workflow.Actions { + // Verify if the action environment exists and append + found := false + for _, env := range allEnvs { + if env.Name == action.Environment { + found = true + + if env.Type == "cloud" { + cloudExec = true + } else if env.Type == "onprem" { + onpremExecution = true + } else { + log.Printf("[ERROR] No handler for environment type %s", env.Type) + return shuffle.WorkflowExecution{}, "No active environments found", errors.New(fmt.Sprintf("No handler for environment type %s", env.Type)) + } + break + } + } + + if !found { + log.Printf("[ERROR] Couldn't find environment %s. Maybe it's inactive?", action.Environment) + return shuffle.WorkflowExecution{}, "Couldn't find the environment", errors.New(fmt.Sprintf("Couldn't find env %s in org %s", action.Environment, workflowExecution.ExecutionOrg)) + } + + found = false + for _, env := range environments { + if env == action.Environment { + + found = true + break + } + } + + // Check if the app exists? + newName := action.AppName + newName = strings.ReplaceAll(newName, " ", "-") + imageNames = append(imageNames, fmt.Sprintf("%s:%s_%s", baseDockerName, newName, action.AppVersion)) + + if !found { + environments = append(environments, action.Environment) + } + } + + err = imageCheckBuilder(imageNames) + if err != nil { + log.Printf("[ERROR] Failed building the required images from %#v: %s", imageNames, err) return shuffle.WorkflowExecution{}, "Failed building missing Docker images", err } @@ -2867,7 +3541,7 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) { } resp.WriteHeader(200) - resp.Write(returnBytes) + resp.Write([]byte(returnBytes)) } // Onlyname is used to diff --git a/backend/tests/cache.sh b/backend/tests/cache.sh old mode 100644 new mode 100755 diff --git a/backend/tests/cleanup.sh b/backend/tests/cleanup.sh old mode 100644 new mode 100755 diff --git a/backend/tests/dockerpull.sh b/backend/tests/dockerpull.sh old mode 100644 new mode 100755 diff --git a/backend/tests/execute.sh b/backend/tests/execute.sh old mode 100644 new mode 100755 diff --git a/backend/tests/file_download.sh b/backend/tests/file_download.sh old mode 100644 new mode 100755 diff --git a/backend/tests/forparser.py b/backend/tests/forparser.py old mode 100644 new mode 100755 diff --git a/backend/tests/hooks.sh b/backend/tests/hooks.sh old mode 100644 new mode 100755 diff --git a/backend/tests/hotload.sh b/backend/tests/hotload.sh old mode 100644 new mode 100755 diff --git a/backend/tests/list_files.sh b/backend/tests/list_files.sh old mode 100644 new mode 100755 diff --git a/backend/tests/migrate_db.sh b/backend/tests/migrate_db.sh old mode 100644 new mode 100755 diff --git a/backend/tests/run_function.py b/backend/tests/run_function.py old mode 100644 new mode 100755 diff --git a/backend/tests/scheduleapps.sh b/backend/tests/scheduleapps.sh old mode 100644 new mode 100755 diff --git a/backend/tests/schedules.sh b/backend/tests/schedules.sh old mode 100644 new mode 100755 diff --git a/backend/tests/sendmail.sh b/backend/tests/sendmail.sh old mode 100644 new mode 100755 diff --git a/backend/tests/testWorkflows.sh b/backend/tests/testWorkflows.sh old mode 100644 new mode 100755 diff --git a/backend/tests/test_wrappers.py b/backend/tests/test_wrappers.py old mode 100644 new mode 100755 diff --git a/backend/tests/triggers.sh b/backend/tests/triggers.sh old mode 100644 new mode 100755 diff --git a/backend/tests/users.sh b/backend/tests/users.sh old mode 100644 new mode 100755 diff --git a/backend/tests/validate_app_values.sh b/backend/tests/validate_app_values.sh old mode 100644 new mode 100755 diff --git a/backend/tests/websocket.sh b/backend/tests/websocket.sh old mode 100644 new mode 100755 diff --git a/backend/tests/workflowdata.json b/backend/tests/workflowdata.json old mode 100644 new mode 100755 diff --git a/backend/tests/workflowresults.sh b/backend/tests/workflowresults.sh old mode 100644 new mode 100755 diff --git a/backend/tests/workflows.sh b/backend/tests/workflows.sh old mode 100644 new mode 100755 diff --git a/docker-compose.yml b/docker-compose.yml old mode 100644 new mode 100755 index 04d07033..daf8f7a0 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - image: ghcr.io/shuffle/shuffle-frontend:latest + image: ghcr.io/shuffle/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -15,7 +15,7 @@ services: depends_on: - backend backend: - image: ghcr.io/shuffle/shuffle-backend:latest + image: ghcr.io/shuffle/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} # Here for debugging: @@ -34,7 +34,7 @@ services: - SHUFFLE_FILE_LOCATION=/shuffle-files restart: unless-stopped orborus: - image: ghcr.io/shuffle/shuffle-orborus:latest + image: ghcr.io/shuffle/shuffle-orborus:nightly container_name: shuffle-orborus hostname: shuffle-orborus networks: @@ -62,7 +62,7 @@ services: container_name: shuffle-opensearch environment: - bootstrap.memory_lock=true - - "OPENSEARCH_JAVA_OPTS=-Xms1024m -Xmx1024m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM + - "OPENSEARCH_JAVA_OPTS=-Xms2048m -Xmx2048m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM - cluster.initial_master_nodes=shuffle-opensearch - cluster.routing.allocation.disk.threshold_enabled=false - cluster.name=shuffle-cluster diff --git a/frontend/Dockerfile b/frontend/Dockerfile old mode 100644 new mode 100755 index b2d4f2ce..46d90c13 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,5 +1,5 @@ # Build environment -FROM node:14 as builder +FROM node:18 as builder RUN mkdir /usr/src/app diff --git a/frontend/README.md b/frontend/README.md old mode 100644 new mode 100755 diff --git a/frontend/build.sh b/frontend/build.sh old mode 100644 new mode 100755 index 9dc0626f..0f536e9d --- a/frontend/build.sh +++ b/frontend/build.sh @@ -1,3 +1,3 @@ -npm run build +yarn run build rm -rf ../backend/go-app/build cp -r build/ ../backend/go-app/build diff --git a/frontend/certs/certreq.csr b/frontend/certs/certreq.csr old mode 100644 new mode 100755 diff --git a/frontend/certs/fullchain.pem b/frontend/certs/fullchain.pem old mode 100644 new mode 100755 diff --git a/frontend/certs/privkey.pem b/frontend/certs/privkey.pem old mode 100644 new mode 100755 diff --git a/frontend/confd/conf.d/nginx.conf.toml b/frontend/confd/conf.d/nginx.conf.toml old mode 100644 new mode 100755 diff --git a/frontend/confd/templates/nginx.conf b/frontend/confd/templates/nginx.conf old mode 100644 new mode 100755 diff --git a/frontend/package.json b/frontend/package.json old mode 100644 new mode 100755 index 600cb60f..157ceefc --- a/frontend/package.json +++ b/frontend/package.json @@ -1,31 +1,25 @@ { "name": "shuffler", "homepage": "https://shuffler.io", - "version": "1.1.4", + "version": "1.3.0", "private": true, "dependencies": { - "@babel/core": "^7.15.8", "@codemirror/commands": "^6.2.2", "@emotion/is-prop-valid": "^1.1.1", "@emotion/react": "^11.7.0", "@emotion/styled": "^11.6.0", "@lezer/highlight": "^1.1.3", - "@material-ui/core": "^4.5.2", - "@material-ui/icons": "^4.5.1", - "@material-ui/lab": "^4.0.0-alpha.58", - "@material-ui/styles": "^4.5.2", - "@material-ui/utils": "^4.11.2", "@metamask/detect-provider": "^1.2.0", - "@mui/icons-material": "^5.2.1", + "@mui/icons-material": "^5.14.0", "@mui/material": "^5.2.3", + "@mui/styles": "^5.14.0", "@mui/x-data-grid": "^5.17.11", "@uiw/codemirror-themes": "^4.19.9", "@uiw/react-codemirror": "^3.2.1", "@use-it/interval": "^1.0.0", "algoliasearch": "^4.13.1", - "babel-eslint": "^10.1.0", "class-transformer": "^0.4.0", - "create-react-app": "^4.0.3", + "create-react-app": "^5.0.1", "cytoscape": "^3.15.1", "cytoscape-clipboard": "^2.2.1", "cytoscape-cxtmenu": "^3.1.1", @@ -40,14 +34,23 @@ "github-markdown-css": "^3.0.1", "import": "0.0.6", "interweave": "^11.2.0", + "jss": "^10.10.0", + "jss-camel-case": "^6.1.0", + "jss-default-unit": "^8.0.2", + "jss-global": "^3.0.0", + "jss-nested": "^6.0.1", + "jss-props-sort": "^6.0.0", + "jss-vendor-prefixer": "^8.0.1", "material-icons": "^0.7.7", "material-icons-react": "^1.0.4", "material-ui-chip-input": "^2.0.0-beta.2", - "material-ui-nested-menu-item": "^1.0.2", "md5-file": "^4.0.0", "mdbreact": "^4.21.1", + "mime": "^3.0.0", "moment": "^2.29.1", - "react": "^16.14.0", + "mui-nested-menu": "^3.2.1", + "process": "^0.11.10", + "react": "^18.2.0", "react-alert": "^5.5.0", "react-alert-template-basic": "^1.0.0", "react-alice-carousel": "^2.6.4", @@ -57,7 +60,7 @@ "react-cookie": "^4.0.1", "react-cytoscapejs": "^1.2.0", "react-device-detect": "^1.9.10", - "react-dom": "^16.14.0", + "react-dom": "^18.2.0", "react-draggable": "^3.3.2", "react-driftjs": "^1.2.2", "react-dropzone": "^10.1.10", @@ -66,12 +69,12 @@ "react-instantsearch-dom": "^6.28.0", "react-json-pretty": "^2.2.0", "react-json-view": "^1.19.1", - "react-markdown": "^4.2.2", + "react-markdown": "^8.0.7", "react-markdown-github": "^3.3.1", "react-powerhooks": "0.0.7", "react-router": "6.2.1", "react-router-dom": "6.2.1", - "react-scripts": "^4.0.1", + "react-scripts": "^5.0.1", "react-shepherd": "^3.3.6", "reactstrap": "^7.1.0", "reaviz": "^12.1.0", @@ -79,14 +82,12 @@ "shellwords": "^0.1.1", "simplebar": "^4.2.3", "styled-components": "^4.4.0", - "webpack": "^4.44.2", - "websocket": "^1.0.30", "yaml": "^1.7.2", "yamljs": "^0.3.0", "zone.js": "~0.11.4" }, "scripts": { - "start": "PORT=3000 react-scripts start", + "start": "HTTPS=false&&PORT=3000 react-scripts --openssl-legacy-provider start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", @@ -108,7 +109,14 @@ "not op_mini all" ], "devDependencies": { + "@babel/core": "^7.15.8", + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "babel-eslint": "^10.1.0", "prettier": "2.4.1", - "promise-window": "^1.2.1" + "promise-window": "^1.2.1", + "react-16": "npm:react@16.13.1", + "react-dom-16": "npm:react-dom@16.13.1", + "react-error-overlay": "6.0.9", + "webpack": "^4.46.0" } } diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico old mode 100644 new mode 100755 diff --git a/frontend/public/images/Shuffle_logo.png b/frontend/public/images/Shuffle_logo.png old mode 100644 new mode 100755 diff --git a/frontend/public/index.html b/frontend/public/index.html old mode 100644 new mode 100755 diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json old mode 100644 new mode 100755 diff --git a/frontend/run.sh b/frontend/run.sh index 0ba2beea..9a608216 100755 --- a/frontend/run.sh +++ b/frontend/run.sh @@ -10,9 +10,9 @@ docker tag ghcr.io/frikky/shuffle-frontend:nightly ghcr.io/shuffle/shuffle-front echo "Starting server" # Rerun build locally for it to update :) -#docker run -it \ -# -p 3001:80 \ -# -p 3002:443 \ -# -v $(pwd)/build:/usr/share/nginx/html:ro \ -# --rm \ -# nginx +docker run -it \ + -p 3001:80 \ + -p 3002:443 \ + -v $(pwd)/build:/usr/share/nginx/html:ro \ + --rm \ + ghcr.io/frikky/shuffle-frontend:nightly diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx old mode 100644 new mode 100755 index d4e116ba..fcbb006c --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,14 +1,12 @@ import React, { useState, useEffect } from "react"; -//import { Route, Routes } from "react-router"; -import { Route, Routes, BrowserRouter } from "react-router-dom"; +import { Link, Route, Routes, BrowserRouter, useNavigate } from "react-router-dom"; import { CookiesProvider } from "react-cookie"; import { removeCookies, useCookies } from "react-cookie"; import Workflows from "./views/Workflows"; import GettingStarted from "./views/GettingStarted"; -import EditWebhook from "./views/EditWebhook"; -import AngularWorkflow from "./views/AngularWorkflow"; +import AngularWorkflow from "./views/AngularWorkflow.jsx"; import Header from "./components/Header.jsx"; import theme from "./theme"; @@ -20,22 +18,20 @@ import Dashboard from "./views/Dashboard.jsx"; import DashboardView from "./views/DashboardViews.jsx"; import AdminSetup from "./views/AdminSetup"; import Admin from "./views/Admin"; -import Docs from "./views/Docs"; -import Introduction from "./views/Introduction"; +import Docs from "./views/Docs.jsx"; +//import Introduction from "./views/Introduction"; import SetAuthentication from "./views/SetAuthentication"; import SetAuthenticationSSO from "./views/SetAuthenticationSSO"; import Search from "./views/Search.jsx"; import RunWorkflow from "./views/RunWorkflow.jsx"; -import LandingPageNew from "./views/LandingpageNew"; import LoginPage from "./views/LoginPage"; import SettingsPage from "./views/SettingsPage"; import KeepAlive from "./views/KeepAlive.jsx"; -import MyView from "./views/MyView"; - -import { createMuiTheme, MuiThemeProvider } from "@material-ui/core/styles"; +import { ThemeProvider } from "@mui/material/styles"; +import UpdateAuthentication from "./views/UpdateAuthentication.jsx"; import FrameworkWrapper from "./views/FrameworkWrapper.jsx"; import ScrollToTop from "./components/ScrollToTop"; import AlertTemplate from "./components/AlertTemplate"; @@ -44,7 +40,6 @@ import { isMobile } from "react-device-detect"; import detectEthereumProvider from "@metamask/detect-provider"; import Drift from "react-driftjs"; -import DashboardPage from "./views/TempDashboard.jsx"; // Production - backend proxy forwarding in nginx var globalUrl = window.location.origin; @@ -284,18 +279,6 @@ const App = (message, props) => { } const includedData = - window.location.pathname === "/home" || - window.location.pathname === "/features" ? ( -
- - } - /> - -
- ) : (
{ /> } /> + } /> + } /> { /> } /> - - } - /> - - } - /> { {...props} /> } - /> - - } /> { />
- ); - //
- // backgroundColor: "#213243", - // This is a mess hahahah return ( - + @@ -728,7 +675,7 @@ const App = (message, props) => { - + ); }; diff --git a/frontend/src/__test__/appdata.js b/frontend/src/__test__/appdata.js old mode 100644 new mode 100755 diff --git a/frontend/src/__test__/environmentdata.js b/frontend/src/__test__/environmentdata.js old mode 100644 new mode 100755 diff --git a/frontend/src/__test__/scheduledata.js b/frontend/src/__test__/scheduledata.js old mode 100644 new mode 100755 diff --git a/frontend/src/__test__/webhookdata.js b/frontend/src/__test__/webhookdata.js old mode 100644 new mode 100755 diff --git a/frontend/src/__test__/workflowdata.js b/frontend/src/__test__/workflowdata.js old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/bag.svg b/frontend/src/assets/img/bag.svg old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/book.svg b/frontend/src/assets/img/book.svg old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/github_phishing_email.png b/frontend/src/assets/img/github_phishing_email.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/github_shuffle_img.png b/frontend/src/assets/img/github_shuffle_img.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/icpl_logo.png b/frontend/src/assets/img/icpl_logo.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/kafka.png b/frontend/src/assets/img/kafka.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/logo.png b/frontend/src/assets/img/logo.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/logo.svg b/frontend/src/assets/img/logo.svg old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/logo.webp b/frontend/src/assets/img/logo.webp old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/mobile.svg b/frontend/src/assets/img/mobile.svg old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/shuffle_adminaccount.png b/frontend/src/assets/img/shuffle_adminaccount.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/shuffle_architecture.png b/frontend/src/assets/img/shuffle_architecture.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/shuffle_webhook.png b/frontend/src/assets/img/shuffle_webhook.png old mode 100644 new mode 100755 diff --git a/frontend/src/assets/img/webhook.png b/frontend/src/assets/img/webhook.png old mode 100644 new mode 100755 diff --git a/frontend/src/charts.js b/frontend/src/charts.js old mode 100644 new mode 100755 diff --git a/frontend/src/components/AlertPopup.js b/frontend/src/components/AlertPopup.js old mode 100644 new mode 100755 diff --git a/frontend/src/components/AlertTemplate.js b/frontend/src/components/AlertTemplate.js old mode 100644 new mode 100755 index 4dfa06d0..3b320a6b --- a/frontend/src/components/AlertTemplate.js +++ b/frontend/src/components/AlertTemplate.js @@ -1,9 +1,14 @@ import React from "react"; -import InfoIcon from "@material-ui/icons/Info"; -import CheckIcon from "@material-ui/icons/Check"; -import ErrorOutlineIcon from "@material-ui/icons/ErrorOutline"; -import CloseIcon from "@material-ui/icons/Close"; -import Typography from "@material-ui/core/Typography"; +import { + Info as InfoIcon, + Check as CheckIcon, + ErrorOutline as ErrorOutlineIcon, + Close as CloseIcon, +} from "@mui/icons-material"; + +import { + Typography +} from "@mui/material"; const alertStyle = { backgroundColor: "rgba(0,0,0,0.9)", diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index e6be25f1..54fb6869 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -23,16 +23,13 @@ import { Dialog, Chip, Avatar, -} from "@material-ui/core"; - -import { Button -} from '@material-ui/core'; +} from "@mui/material"; import { Close as CloseIcon, Delete as DeleteIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import * as edgehandles from "cytoscape-edgehandles"; import * as cytoscape from "cytoscape"; diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index af7ddae5..1e6ab296 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -1,10 +1,14 @@ import React, {useEffect, useState} from 'react'; +import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { + Search as SearchIcon, + CloudQueue as CloudQueueIcon, + Code as CodeIcon +} from '@mui/icons-material'; import algoliasearch from 'algoliasearch/lite'; import { InstantSearch, Configure, connectSearchBox, connectHits, connectHitInsights } from 'react-instantsearch-dom'; @@ -21,7 +25,7 @@ import { Typography, Button, Tooltip -} from '@material-ui/core'; +} from '@mui/material'; const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") @@ -34,7 +38,6 @@ const AppGrid = props => { const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); diff --git a/frontend/src/components/AppGrid1.jsx b/frontend/src/components/AppGrid1.jsx deleted file mode 100644 index b64610c5..00000000 --- a/frontend/src/components/AppGrid1.jsx +++ /dev/null @@ -1,377 +0,0 @@ -import React, {useEffect, useState} from 'react'; - -import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; -import {Link} from 'react-router-dom'; - -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; - -import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch, Configure, connectSearchBox, connectHits, connectHitInsights } from 'react-instantsearch-dom'; - -import aa from 'search-insights' - -import { - Zoom, - Grid, - Paper, - TextField, - ButtonBase, - InputAdornment, - Typography, - Button, - Tooltip -} from '@material-ui/core'; - -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") -//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") -const AppGrid1 = props => { - const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, searchValue } = props - - const isCloud = - window.location.host === "localhost:3000" || - window.location.host === "shuffler.io"; - - const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows - const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : 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(""); - - const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} - - const innerColor = "rgba(255,255,255,0.65)" - const borderRadius = 3 - window.title = "Shuffle | Apps | Find and integrate any app" - - const submitContact = (email, message) => { - const data = { - "firstname": "", - "lastname": "", - "title": "", - "companyname": "", - "email": email, - "phone": "", - "message": message, - } - - const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly." - - fetch(globalUrl+"/api/v1/contact", { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }) - .then(response => response.json()) - .then(response => { - if (response.success === true) { - setFormMessage(response.reason) - //alert.info("Thanks for submitting!") - } else { - setFormMessage(errorMessage) - } - - setFormMail("") - setMessage("") - }) - .catch(error => { - setFormMessage(errorMessage) - console.log(error) - }); - } - - const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { - - useEffect(() => { - if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { - const urlSearchParams = new URLSearchParams(window.location.search) - const params = Object.fromEntries(urlSearchParams.entries()) - const foundQuery = {searchValue} - if (foundQuery !== null && foundQuery !== undefined) { - console.log("Got query: ", foundQuery) - refine(foundQuery) - } - } - }, []) - - return ( -
- - - - ), - }} - autoComplete='off' - type="hidden" - color="primary" - defaultValue={currentRefinement} - placeholder="Find Apps..." - id="shuffle_search_field" - onChange={(event) => { - refine(event.currentTarget.value) - }} - limit={5} - /> - {/*isSearchStalled ? 'My search is stalled' : ''*/} - - ) - } - - var workflowDelay = -50 - const Hits = ({ hits, insights }) => { - const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) - var counted = 0 - - //console.log(hits) - //var curhits = hits - //if (hits.length > 0 && defaultApps.length === 0) { - // setDefaultApps(hits) - //} - - //const [defaultApps, setDefaultApps] = React.useState([]) - //console.log(hits) - //if (hits.length > 0 && hits.length !== innerHits.length) { - // setInnerHits(hits) - //} - - return ( - - {hits.map((data, index) => { - - workflowDelay += 50 - - const paperStyle = { - backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, - color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", - border: `1px solid ${innerColor}`, - padding: 15, - cursor: "pointer", - position: "relative", - minHeight: 116, - } - - if (counted === 12/xs*rowHandler) { - return null - } - - counted += 1 - var parsedname = "" - for (var key = 0; key < data.name.length; key++) { - var character = data.name.charAt(key) - if (character === character.toUpperCase()) { - //console.log(data.name[key], data.name[key+1]) - if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) { - } else { - parsedname += " " - } - } - - parsedname += character - } - - parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ") - - return ( - - - - { - setMouseHoverIndex(index) - /* - ReactGA.event({ - category: "app_grid_view", - action: `search_bar_click`, - label: "", - }) - */ - }} onMouseOut={() => { - setMouseHoverIndex(-1) - }} onClick={() => { - if (isCloud) { - ReactGA.event({ - category: "app_grid_view", - action: `app_${parsedname}_${data.id}_click`, - label: "", - }) - } - - //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") - console.log(searchClient) - aa('init', { - appId: searchClient.appId, - apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"] - }) - - const timestamp = new Date().getTime() - aa('sendEvents', [ - { - eventType: 'click', - eventName: 'Product Clicked', - index: 'appsearch', - objectIDs: [data.objectID], - timestamp: timestamp, - queryID: data.__queryID, - positions: [data.__position], - userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id, - } - ]) - - }}> - - {data.name} - -
- {index === mouseHoverIndex || showName === true ? - parsedname - : - null - } - {data.generated ? - - {data.invalid ? - - : - - } - - : - - - - } - - - - - ) - })} - - ) - } - - const CustomSearchBox = connectSearchBox(SearchBox) - const CustomHits = connectHits(Hits) - //const CustomHits = connectHitInsights(aa)(Hits) - const selectButtonStyle = { - minWidth: 150, - maxWidth: 150, - minHeight: 50, - } - - return ( -
- {/* -
- -
- */} -
- -
- -
- - -
- {showSuggestion === true ? -
- - Can't find what you're looking for? - -
- setFormMail(e.target.value)} - /> - setMessage(e.target.value)} - /> -
- - {formMessage} -
- : null - } - - - - Search by - - - Algolia logo - - -
-
- ) -} - -export default AppGrid1; diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index 9da69a70..5defd0f0 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -1,14 +1,14 @@ import React, { useState, useEffect } from 'react'; +import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; import { useAlert } from "react-alert"; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; //import algoliasearch from 'algoliasearch/lite'; import algoliasearch from 'algoliasearch'; import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom'; -import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@material-ui/core'; +import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@mui/material'; import aa from 'search-insights' const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const Appsearch = props => { @@ -18,7 +18,6 @@ const Appsearch = props => { const alert = useAlert(); 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(""); diff --git a/frontend/src/components/AppsearchPopout.jsx b/frontend/src/components/AppsearchPopout.jsx index 25ea719c..1e76206b 100644 --- a/frontend/src/components/AppsearchPopout.jsx +++ b/frontend/src/components/AppsearchPopout.jsx @@ -12,12 +12,12 @@ import { CircularProgress, Tooltip, Button, -} from "@material-ui/core"; +} from "@mui/material"; import { Close as CloseIcon, Delete as DeleteIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const AppSearchPopout = (props) => { const { diff --git a/frontend/src/components/AuthenticationItem.jsx b/frontend/src/components/AuthenticationItem.jsx index eef9806c..06329a35 100644 --- a/frontend/src/components/AuthenticationItem.jsx +++ b/frontend/src/components/AuthenticationItem.jsx @@ -19,13 +19,13 @@ import { Typography, TextField, Zoom, -} from "@material-ui/core"; +} from "@mui/material"; import { Edit as EditIcon, Delete as DeleteIcon, SelectAll as SelectAllIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const AuthenticationItem = (props) => { const { data, index, globalUrl, getAppAuthentication } = props diff --git a/frontend/src/components/AuthenticationNormal.jsx b/frontend/src/components/AuthenticationNormal.jsx index 5d3a4fd3..a0d6acab 100644 --- a/frontend/src/components/AuthenticationNormal.jsx +++ b/frontend/src/components/AuthenticationNormal.jsx @@ -13,11 +13,11 @@ import { DialogTitle, DialogContent, Typography, -} from "@material-ui/core"; +} from "@mui/material"; import { LockOpen as LockOpenIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const AuthenticationData = (props) => { const { diff --git a/frontend/src/components/AuthenticationWindow.jsx b/frontend/src/components/AuthenticationWindow.jsx new file mode 100755 index 00000000..47dd9b4e --- /dev/null +++ b/frontend/src/components/AuthenticationWindow.jsx @@ -0,0 +1,476 @@ +import React, { useState, useEffect } from "react"; + +import theme from '../theme.jsx'; +import { v4 as uuidv4 } from "uuid"; +import { useAlert } from "react-alert"; + +import { + Divider, + MenuItem, + Button, + Dialog, + DialogTitle, + DialogActions, + DialogContent, + Textfield, + TextField, + Typography, + Select, + IconButton, +} from "@mui/material"; + +import { + LockOpen as LockOpenIcon, + Close as CloseIcon, +} from "@mui/icons-material"; + +import PaperComponent from "../components/PaperComponent.jsx" +import { useParams, useNavigate, Link } from "react-router-dom"; + +const AuthenticationData = (props) => { + const { + globalUrl, + selectedApp, + getAppAuthentication, + authenticationModalOpen, + setAuthenticationModalOpen, + + configureWorkflowModalOpen, + workflow, + setUpdate, + selectedAction, + setSelectedAction, + isLoggedIn, + authFieldsOnly, + } = props + + const alert = useAlert() + let navigate = useNavigate(); + const [submitSuccessful, setSubmitSuccessful] = useState(false) + const [authenticationOption, setAuthenticationOptions] = React.useState({ + app: JSON.parse(JSON.stringify(selectedApp)), + fields: {}, + label: "", + usage: [ + { + workflow_id: workflow === undefined ? "" : workflow.id, + }, + ], + id: uuidv4(), + active: true, + }); + + useEffect(() => { + if (isLoggedIn === false && authFieldsOnly !== true) { + navigate(`/login?view=${window.location.pathname}&message=Log in to authenticate this app`) + } + }, []) + + const setNewAppAuth = (appAuthData) => { + var headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + + // Find org_id and authorization from queries and add to headers + if (window.location.search !== "") { + const params = new URLSearchParams(window.location.search) + const org_id = params.get("org_id") + const authorization = params.get("authorization") + if (org_id !== null && authorization !== null) { + headers["Org-Id"] = org_id + headers["Authorization"] = "Bearer " + authorization + } + } + + fetch(globalUrl + "/api/v1/apps/authentication", { + method: "PUT", + headers: headers, + body: JSON.stringify(appAuthData), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for setting app auth :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + if (responseJson.reason === undefined) { + alert.error("Failed to set app auth. Are you logged in?") + } else { + alert.error("Failed to set app auth: " + responseJson.reason); + } + } else { + setSubmitSuccessful(true) + if (getAppAuthentication !== undefined) { + getAppAuthentication(true, false); + } + + if (setAuthenticationModalOpen !== undefined) { + setAuthenticationModalOpen(false) + } + } + }) + .catch((error) => { + //alert.error(error.toString()); + console.log("New auth error: ", error.toString()); + }); + }; + + if (selectedApp.authentication === undefined || selectedApp.authentication.parameters === null || + selectedApp.authentication.parameters === undefined || selectedApp.authentication.parameters.length === 0) { + + return ( + + + {selectedApp.name} does not require authentication + + + ); + } + + authenticationOption.app.actions = []; + + for (let paramkey in selectedApp.authentication.parameters) { + if ( + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] === undefined + ) { + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] = ""; + } + } + + const handleSubmitCheck = () => { + if (authenticationOption.label.length === 0) { + authenticationOption.label = `Auth for ${selectedApp.name}`; + } + + // Automatically mapping fields that already exist (predefined). + // Warning if fields are NOT filled + for (let paramkey in selectedApp.authentication.parameters) { + if ( + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ].length === 0 + ) { + if ( + selectedApp.authentication.parameters[paramkey].value !== undefined && + selectedApp.authentication.parameters[paramkey].value !== null && + selectedApp.authentication.parameters[paramkey].value.length > 0 + ) { + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] = selectedApp.authentication.parameters[paramkey].value; + } else { + if ( + selectedApp.authentication.parameters[paramkey].schema.type === "bool" + ) { + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] = "false"; + } else { + alert.info( + "Field " + + selectedApp.authentication.parameters[paramkey].name + + " can't be empty" + ); + return; + } + } + } + } + + console.log("Action: ", selectedAction); + + if (selectedAction !== undefined) { + selectedAction.authentication_id = authenticationOption.id; + selectedAction.selectedAuthentication = authenticationOption; + if ( + selectedAction.authentication === undefined || + selectedAction.authentication === null + ) { + selectedAction.authentication = [authenticationOption]; + } else { + selectedAction.authentication.push(authenticationOption); + } + + setSelectedAction(selectedAction); + } + + var newAuthOption = JSON.parse(JSON.stringify(authenticationOption)); + var newFields = []; + console.log("Fields: ", newAuthOption.fields) + for (let authkey in newAuthOption.fields) { + const value = newAuthOption.fields[authkey]; + newFields.push({ + "key": authkey, + "value": value, + }); + } + + newAuthOption.fields = newFields; + setNewAppAuth(newAuthOption); + + if (configureWorkflowModalOpen === true) { + setSelectedAction({}); + } + + if (setUpdate !== undefined) { + setUpdate(authenticationOption.id); + } + }; + + if (authenticationOption.label === null || authenticationOption.label === undefined) { + authenticationOption.label = selectedApp.name + " authentication"; + } + + const authenticationParameters = selectedApp.authentication.parameters.map((data, index) => { + return ( +
+
+ + + {data.name.replace("_basic", "", -1).replace("_", " ", -1)} + +
+ + {data.schema !== undefined && + data.schema !== null && + data.schema.type === "bool" ? ( + + ) : ( + { + authenticationOption.fields[data.name] = + event.target.value; + }} + /> + )} +
+ ); + }) + + const authenticationButtons = + + {authFieldsOnly === true ? null : + + } + + + // Check if only the auth items should show + if (authFieldsOnly === true) { + return ( +
+ {submitSuccessful === true ? + + App succesfully configured! You may close this window. + + : + + {authenticationParameters} + {authenticationButtons} + + } +
+ ) + } + + return ( + { + //if (configureWorkflowModalOpen) { + // setSelectedAction({}); + //} + setAuthenticationModalOpen(false); + }} + PaperProps={{ + style: { + pointerEvents: "auto", + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: 600, + minHeight: 600, + maxHeight: 600, + padding: 15, + overflow: "hidden", + zIndex: 10012, + border: theme.palette.defaultBorder, + }, + }} + > + { + setAuthenticationModalOpen(false); + if (configureWorkflowModalOpen === true) { + setSelectedAction({}); + } + }} + > + + + +
+ Authentication for {selectedApp.name} +
+
+ + + What is app authentication? + +
+ These are required fields for authenticating with {selectedApp.name} +
+ Name - what is this used for? + { + authenticationOption.label = event.target.value; + }} + /> + +
+ {authenticationParameters} + + + + {authenticationButtons} + +
+ ); +}; + +export default AuthenticationData; diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index b59b5e4a..71d01789 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; +import theme from "../theme.jsx"; import ReactGA from 'react-ga4'; -import { useTheme } from "@material-ui/core/styles"; import { Paper, Typography, @@ -9,7 +9,7 @@ import { Button, Grid, Card, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; @@ -17,7 +17,6 @@ import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; const Billing = (props) => { const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; console.log("Billing: ", billingInfo); - const theme = useTheme(); const alert = useAlert(); const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : "" diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index 40254bbb..4b44f5c1 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -2,7 +2,6 @@ import React, { useState, useEffect } from "react"; import ReactGA from 'react-ga4'; import theme from "../theme.jsx"; -import { useTheme } from "@material-ui/core/styles"; import { Paper, Typography, @@ -10,7 +9,7 @@ import { Button, Grid, Card, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 873f9f7e..62394423 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -15,7 +15,7 @@ import { Dialog, DialogTitle, DialogActions, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; import { @@ -40,19 +40,38 @@ import { Business as BusinessIcon, Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; + +const scrollStyle1 = { + height: 100, + width: 225, + overflow: "hidden", + position: "relative", +} + +const scrollStyle2 = { + position: "absolute", + top: 0, + left: 0, + bottom: "-20px", + right: "-20px", + overflow: "scroll", +} const CacheView = (props) => { const { globalUrl, userdata, serverside, orgId } = props; const [orgCache, setOrgCache] = React.useState(""); const [listCache, setListCache] = React.useState([]); const [addCache, setAddCache] = React.useState(""); + const [editedCache, setEditedCache] = React.useState(""); const [modalOpen, setModalOpen] = React.useState(false); - const [key, setKey]= React.useState(""); - const [value, setValue]= React.useState(""); - const [cacheInput, setCacheInput]= React.useState(""); - const [cacheCursor, setCacheCursor]= React.useState(""); - + const [key, setKey] = React.useState(""); + const [value, setValue] = React.useState(""); + const [cacheInput, setCacheInput] = React.useState(""); + const [cacheCursor, setCacheCursor] = React.useState(""); + const [dataValue, setDataValue] = React.useState({}); + const [editCache, setEditCache] = React.useState(false); + const [show, setShow] = useState({}); const alert = useAlert(); useEffect(() => { listOrgCache(orgId); @@ -68,26 +87,26 @@ const CacheView = (props) => { }, credentials: "include", }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for apps :O!"); - return; - } + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for apps :O!"); + return; + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === true) { - setListCache(responseJson.keys); - } + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === true) { + setListCache(responseJson.keys); + } - if (responseJson.cursor !== undefined && responseJson.cursor !== null && responseJson.cursor !== "") { - setCacheCursor(responseJson.cursor); - } - }) - .catch((error) => { - alert.error(error.toString()); - }); + if (responseJson.cursor !== undefined && responseJson.cursor !== null && responseJson.cursor !== "") { + setCacheCursor(responseJson.cursor); + } + }) + .catch((error) => { + alert.error(error.toString()); + }); }; // const getCacheList = (orgId) => { @@ -103,8 +122,8 @@ const CacheView = (props) => { // if (response.status !== 200) { // console.log("Status not 200 for WORKFLOW EXECUTION :O!"); // } - - + + // return response.json(); // }) // .then((responseJson) => { @@ -126,37 +145,72 @@ const CacheView = (props) => { // console.log("Get userprofile error: ", error); // }) // } - + const deleteCache = (orgId, key) => { alert.info("Attempting to delete Cache"); fetch(globalUrl + `/api/v1/orgs/${orgId}/cache/${key}`, { - method: "DELETE", - headers: { - Accept: "application/json", - }, - credentials: "include", + method: "DELETE", + headers: { + Accept: "application/json", + }, + credentials: "include", }) - .then((response) => { - if (response.status === 200) { - alert.success("Successfully deleted Cache"); - setTimeout(() => { + .then((response) => { + if (response.status === 200) { + alert.success("Successfully deleted Cache"); + setTimeout(() => { + listOrgCache(orgId); + }, 1000); + } else { + alert.error("Failed deleting Cache. Does it still exist?"); + } + }) + .catch((error) => { + alert.error(error.toString()); + }); + }; + + const editOrgCache = (orgId) => { + const cache = { key: dataValue.key , value: value }; + setCacheInput([cache]); + console.log("cache:", cache) + console.log("cache input: ", cacheInput) + + fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, { + + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(cache), + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for Cache :O!"); + return; + } + + return response.json(); + }) + .then((responseJson) => { + setAddCache(responseJson); + alert.success("Cache Edited Successfully!"); listOrgCache(orgId); - }, 1000); - } else { - alert.error("Failed deleting Cache. Does it still exist?"); - } - }) - .catch((error) => { - alert.error(error.toString()); - }); - }; + setModalOpen(false); + }) + .catch((error) => { + alert.error(error.toString()); + }); + }; const addOrgCache = (orgId) => { - const cache={key:key,value:value}; - setCacheInput([cache]); - console.log("cache input:",cacheInput) - + const cache = { key: key, value: value }; + setCacheInput([cache]); + console.log("cache input:", cacheInput) + fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, { method: "POST", @@ -187,6 +241,8 @@ const CacheView = (props) => { }; const modalView = ( + // console.log("key:", dataValue.key), + //console.log("value:",dataValue.value), { @@ -203,10 +259,10 @@ const CacheView = (props) => { > - Add Cache + { editCache ? "Edit Cache" : "Add Cache" } -
+
Key { fullWidth={true} autoComplete="Key" placeholder="abc" - id="keyfield" + id="keyfield" margin="normal" variant="outlined" - value={key} - onChange={(e)=>setKey(e.target.value)} + value={editCache ? dataValue.key : key} + onChange={(e) => setKey(e.target.value)} />
-
+
Value { id="Valuefield" margin="normal" variant="outlined" - value={value} - onChange={(e)=>setValue(e.target.value)} + defaultValue={editCache ? dataValue.value : ""} + onChange={(e) => setValue(e.target.value)} />
- +
@@ -282,7 +338,7 @@ const CacheView = (props) => {

Shuffle Datastore

- Datastore is a key-value store for storing data that can be used cross-workflow.  + Datastore is a key-value store for storing data that can be used cross-workflow.  { style={{}} variant="contained" color="primary" - onClick={() => setModalOpen(true)} + onClick={() =>{ + setEditCache(false) + setModalOpen(true) + } + } > Add Cache @@ -319,19 +379,19 @@ const CacheView = (props) => { {listCache === undefined || listCache === null @@ -345,27 +405,41 @@ const CacheView = (props) => { return ( +
+ // onMouseOver={() => + // setShow((prevState) => ({ ...prevState, [data.value]: true })) + // } + // onMouseLeave={() => + // setShow((prevState) => ({ ...prevState, [data.value]: false })) + // } + //primary={show[data.value] ? data.value : `${data.value.substring(0, 5)}...`} + primary={data.value} + /> +
@@ -377,7 +451,7 @@ const CacheView = (props) => { paddingLeft: "155px", }} primary= - {/* { { - + setEditCache(true) + setDataValue({"key":data.key,"value":data.value}) + setModalOpen(true) }} > { /> - */} + { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); diff --git a/frontend/src/components/DocsGrid.jsx b/frontend/src/components/DocsGrid.jsx index ea20cd0e..fa194a25 100644 --- a/frontend/src/components/DocsGrid.jsx +++ b/frontend/src/components/DocsGrid.jsx @@ -1,10 +1,10 @@ import React, {useEffect, useState} from 'react'; +import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; import aa from 'search-insights' import algoliasearch from 'algoliasearch/lite'; @@ -24,16 +24,15 @@ import { ListItem, ListItemAvatar, ListItemText, -} from '@material-ui/core'; +} from '@mui/material'; -import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons' + const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const DocsGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); @@ -182,7 +181,7 @@ const DocsGrid = props => { //const secondaryText = data.data !== undefined ? data.data.slice(0, 100)+"..." : "" const secondaryText = data.data !== undefined ? data.data.slice(0, 100)+"..." : "" - const baseImage = + const baseImage = const avatar = data.image_url === undefined ? baseImage : diff --git a/frontend/src/components/Dropzone.jsx b/frontend/src/components/Dropzone.jsx old mode 100644 new mode 100755 index 6ea9f0de..911bd87b --- a/frontend/src/components/Dropzone.jsx +++ b/frontend/src/components/Dropzone.jsx @@ -1,6 +1,8 @@ import React, { useRef, useState } from "react"; import { useEffect } from "react"; -import BackupIcon from "@material-ui/icons/Backup"; +import { + Backup as BackupIcon +} from "@mui/icons-material"; const dragOverStyle = { backgroundColor: "rgba(0,0,0,0.8)", diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 22a9d9ea..5e8b0493 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -37,14 +37,14 @@ import { FormControl, FormLabel, -} from "@material-ui/core"; +} from "@mui/material"; import { ExpandLess as ExpandLessIcon, ExpandMore as ExpandMoreIcon, Publish as PublishIcon, OpenInNew as OpenInNewIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const EditWorkflow = (props) => { const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index a3f4e520..cd7a004b 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -15,7 +15,7 @@ import { Divider, Select, MenuItem, -} from "@material-ui/core"; +} from "@mui/material"; import { OpenInNew as OpenInNewIcon, @@ -27,7 +27,7 @@ import { Publish as PublishIcon, Clear as ClearIcon, Add as AddIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import { useAlert } from "react-alert"; import Dropzone from "../components/Dropzone.jsx"; diff --git a/frontend/src/components/FooterNew.js b/frontend/src/components/FooterNew.js old mode 100644 new mode 100755 diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js deleted file mode 100644 index 0934f2e6..00000000 --- a/frontend/src/components/Header.js +++ /dev/null @@ -1,883 +0,0 @@ -import React, { useState } from "react"; -import { BrowserView, MobileView } from "react-device-detect"; - -import { Link } from "react-router-dom"; - -import { useTheme } from "@material-ui/core/styles"; - -import { - Chip, - Badge, - Typography, - Paper, - Tooltip, - List, - Avatar, - Menu, - ListItem, - MenuItem, - Select, - Button, - IconButton, - Grid, -} from "@material-ui/core"; - -import { - MeetingRoom as MeetingRoomIcon, - Settings as SettingsIcon, - Notifications as NotificationsIcon, - Home as HomeIcon, - Polymer as PolymerIcon, - Apps as AppsIcon, - Description as DescriptionIcon, - HelpOutline as HelpOutlineIcon, -} from "@material-ui/icons"; - -import { - Analytics as AnalyticsIcon, - Lightbulb as LightbulbIcon, -} from "@mui/icons-material"; -//import LogoutIcon from '@mui/icons-material/Logout'; -import { useAlert } from "react-alert"; -import SearchField from '../components/Searchfield' - -const hoverColor = "#f85a3e"; -const hoverOutColor = "#e8eaf6"; - -const Header = (props) => { - const { - globalUrl, - setNotifications, - notifications, - isLoggedIn, - removeCookie, - homePage, - isLoaded, - userdata, - cookies, - } = props; - const theme = useTheme(); - - const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); - const [SoarHoverColor, setSoarHoverColor] = useState(hoverOutColor); - const [LoginHoverColor, setLoginHoverColor] = useState(hoverOutColor); - const [DocsHoverColor, setDocsHoverColor] = useState(hoverOutColor); - const [HelpHoverColor, setHelpHoverColor] = useState(hoverOutColor); - const [anchorEl, setAnchorEl] = React.useState(null); - const [anchorElAvatar, setAnchorElAvatar] = React.useState(null); - const alert = useAlert(); - - const hrefStyle = { - color: hoverOutColor, - textDecoration: "none", - }; - - const handleClose = () => { - setAnchorEl(null); - setAnchorElAvatar(null); - }; - - const clearNotifications = () => { - // Don't really care about the logout - fetch(`${globalUrl}/api/v1/notifications/clear`, { - credentials: "include", - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) - .then(function (response) { - if (response.status !== 200) { - console.log("Error in response"); - } - - return response.json(); - }) - .then(function (responseJson) { - if (responseJson.success === true) { - setNotifications([]); - handleClose(); - } else { - alert.error( - "Failed dismissing notifications. Please try again later." - ); - } - }) - .catch((error) => { - console.log("error in notification dismissal: ", error); - //removeCookie("session_token", {path: "/"}) - }); - }; - - const dismissNotification = (alert_id) => { - // Don't really care about the logout - fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { - credentials: "include", - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) - .then(function (response) { - if (response.status !== 200) { - console.log("Error in response"); - } - - return response.json(); - }) - .then(function (responseJson) { - if (responseJson.success === true) { - const newNotifications = notifications.filter( - (data) => data.id !== alert_id - ); - console.log("NEW NOTIFICATIONS: ", newNotifications); - setNotifications(newNotifications); - } else { - alert.error( - "Failed dismissing notification. Please try again later." - ); - } - }) - .catch((error) => { - console.log("error in notification dismissal: ", error); - //removeCookie("session_token", {path: "/"}) - }); - }; - - // DEBUG HERE - const handleClickLogout = () => { - console.log("COOKIES: ", cookies, "Remover: ", removeCookie); - // Don't really care about the logout - fetch(globalUrl + "/api/v1/logout", { - credentials: "include", - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }) - .then(() => { - // Log out anyway - //cookies.remove("session_token") - //window.location.pathname = "/" - console.log("Should've logged out"); - removeCookie("session_token", { path: "/" }); - removeCookie("session_token", { path: "/workflows" }); - window.location.reload(); - }) - .catch((error) => { - console.log("Error in logout: ", error); - removeCookie("session_token", { path: "/" }); - window.location.reload(); - //removeCookie("session_token", {path: "/"}) - }); - }; - - const handleClickChangeOrg = (orgId) => { - // Don't really care about the logout - //name: org.name, - //orgId = "asd" - const data = { - org_id: orgId, - }; - - localStorage.setItem("getting_started_sidebar", "open"); - - fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { - mode: "cors", - method: "POST", - body: JSON.stringify(data), - credentials: "include", - crossDomain: true, - withCredentials: true, - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - }) - .then(function (response) { - if (response.status !== 200) { - console.log("Error in response"); - } - - return response.json(); - }) - .then(function (responseJson) { - if (responseJson.success !== undefined && responseJson.success) { - setTimeout(() => { - window.location.reload(); - }, 2000); - alert.success( - "Successfully changed active organization - refreshing!" - ); - } else { - alert.error("Failed changing org: ", responseJson.reason); - } - }) - .catch((error) => { - console.log("error changing: ", error); - //removeCookie("session_token", {path: "/"}) - }); - }; - - // Rofl this is weird - const handleDocsHover = () => { - setDocsHoverColor(hoverColor); - }; - - const handleDocsHoverOut = () => { - setDocsHoverColor(hoverOutColor); - }; - - const handleHomeHover = () => { - setHomeHoverColor(hoverColor); - }; - - const handleHelpHover = () => { - setHelpHoverColor(hoverColor); - }; - - const handleHelpHoverOut = () => { - setHelpHoverColor(hoverOutColor); - }; - - const handleSoarHover = () => { - setSoarHoverColor(hoverColor); - }; - - const handleSoarHoverOut = () => { - setSoarHoverColor(hoverOutColor); - }; - - const handleHomeHoverOut = () => { - setHomeHoverColor(hoverOutColor); - }; - - const handleLoginHover = () => { - setLoginHoverColor(hoverColor); - }; - - const handleLoginHoverOut = () => { - setLoginHoverColor(hoverOutColor); - }; - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const chipStyle = { - backgroundColor: "#3d3f43", - height: 30, - marginRight: 5, - paddingLeft: 5, - paddingRight: 5, - height: 28, - cursor: "pointer", - borderColor: "#3d3f43", - color: "white", - }; - - const notificationWidth = 350; - const NotificationItem = (props) => { - const { data } = props; - - return ( - - {/* - {new Date(data.updated_at).toISOString()} - */} - {data.reference_url !== undefined && - data.reference_url !== null && - data.reference_url.length > 0 ? ( - - {data.title} - - ) : ( - {data.title} - )} - - {data.image !== undefined && - data.image !== null && - data.image.length > 0 ? ( - {data.title} - ) : null} - {data.description} - {/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ? - data.tags.map((tag, index) => { - return ( - { - }} - variant="outlined" - color="primary" - /> - ) - }) - : null */} - {data.read === false ? ( - - ) : null} - - ); - }; - - const notificationMenu = ( - - { - setAnchorEl(event.currentTarget); - }} - > - - - - - { - handleClose(); - }} - > - -
- - Your Notifications ({notifications.length}) - - {notifications.length > 1 ? ( - - ) : null} -
- - Notifications are made by Shuffle to help you discover issues or - improvements. - -
- {notifications.map((data, index) => { - return ; - })} -
-
- ); - - // Should be based on some path - const avatarMenu = ( - - { - setAnchorElAvatar(event.currentTarget); - }} - > - - - { - handleClose(); - }} - > - { - event.preventDefault(); - handleClose(); - }} - > - - About - - - { - event.preventDefault(); - handleClose(); - }} - > - - Get Started - - - { - event.preventDefault(); - handleClose(); - }} - > - - Use Cases - - - { - event.preventDefault(); - handleClose(); - }} - > - - Settings - - - { - event.preventDefault(); - handleClose(); - handleClickLogout(); - }} - > -  Logout - - - - ); - - // Handle top bar or something - const logoCheck = !homePage ? null : null; - //
- const loginTextBrowser = !isLoggedIn ? ( -
- - - -
- About -
- -
-
- {!isLoaded ? null : - userdata.chat_disabled === true ? null : -
- -
- } -
- - - -
- Login -
- -
-
-
-
- ) : ( -
- - {!isLoaded ? null : - userdata.chat_disabled === true ? null : -
- -
- } -
- {avatarMenu} - {notificationMenu} - {userdata === undefined || - userdata.admin === undefined || - userdata.admin === null || - !userdata.admin ? null : ( - - - - )} - {userdata === undefined || - userdata.orgs === undefined || - userdata.orgs === null || - userdata.orgs.length <= 1 ? null : ( - - )} -
-
- ); - - //console.log("USR: ", userdata.orgs) - - const loginTextMobile = !isLoggedIn ? ( -
- - - -
- - - - - -
- -
- - -
- About -
- -
-
-
- ) : ( -
-
- - - -
- Shuffle -
- -
- - -
- Workflows -
- -
- - -
- Apps -
- -
- {/* - - -
Configure
- -
- */} -
-
-
- {avatarMenu} -
-
- ); - - // - const loadedCheck = ( -
- {loginTextBrowser} - {loginTextMobile} -
- ); - //
- return ( -
- {loadedCheck} -
- ); -}; - -export default Header; diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 975df6b7..49115072 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -1,6 +1,6 @@ import React, {useState} from 'react'; import {BrowserView, MobileView} from "react-device-detect"; -import { useTheme } from '@material-ui/core/styles'; +import theme from '../theme.jsx'; import {Link} from 'react-router-dom'; import ReactGA from 'react-ga4'; @@ -21,7 +21,7 @@ import { IconButton, Divider, LinearProgress, -} from '@material-ui/core' +} from '@mui/material'; import { MeetingRoom as MeetingRoomIcon, @@ -34,12 +34,9 @@ import { Description as DescriptionIcon, EmojiObjects as EmojiObjectsIcon, Business as BusinessIcon, -} from '@material-ui/icons'; - -import { Analytics as AnalyticsIcon, Lightbulb as LightbulbIcon, -} from "@mui/icons-material"; +} from '@mui/icons-material'; import { useAlert } from "react-alert"; @@ -49,7 +46,6 @@ const hoverOutColor = "#e8eaf6" const Header = props => { const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props; - const theme = useTheme(); const alert = useAlert() diff --git a/frontend/src/components/LandingpageUsecases.jsx b/frontend/src/components/LandingpageUsecases.jsx index 9fd5d016..063e4a6b 100644 --- a/frontend/src/components/LandingpageUsecases.jsx +++ b/frontend/src/components/LandingpageUsecases.jsx @@ -4,7 +4,7 @@ import AppFramework, { usecases } from "../components/AppFramework.jsx"; import {Link} from 'react-router-dom'; import ReactGA from 'react-ga4'; -import { Button, LinearProgress, Typography } from '@material-ui/core'; +import { Button, LinearProgress, Typography } from '@mui/material'; export const securityFramework = [ { diff --git a/frontend/src/components/LoginPopup.js b/frontend/src/components/LoginPopup.js old mode 100644 new mode 100755 diff --git a/frontend/src/components/NestedMenu.jsx b/frontend/src/components/NestedMenu.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/components/Newsletter.jsx b/frontend/src/components/Newsletter.jsx index ccadf77b..36a7519c 100644 --- a/frontend/src/components/Newsletter.jsx +++ b/frontend/src/components/Newsletter.jsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import { useTheme } from '@material-ui/core/styles'; +import theme from '../theme.jsx'; import {isMobile} from "react-device-detect"; import ReactGA from 'react-ga4'; @@ -8,7 +8,6 @@ import {TextField, Typography, Button} from '@material-ui/core'; const Newsletter = (props) => { const { globalUrl, } = props; - const theme = useTheme(); const [email, setEmail] = useState(""); const [msg, setMsg] = useState(""); const [buttonActive, setButtonActive] = useState(true); diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx old mode 100644 new mode 100755 index c28f24c7..69b3a699 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -1,7 +1,7 @@ import React, { useRef, useState, useEffect, useLayoutEffect } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; -import { useTheme } from "@material-ui/core/styles"; import theme from '../theme.jsx'; +import { useAlert } from "react-alert"; import { v4 as uuidv4 } from "uuid"; import { @@ -37,7 +37,7 @@ import { CircularProgress, Switch, Fade, -} from "@material-ui/core"; +} from "@mui/material"; import { LockOpen as LockOpenIcon, SupervisorAccount as SupervisorAccountIcon, @@ -97,6 +97,7 @@ const AuthenticationOauth2 = (props) => { } = props; let navigate = useNavigate(); + const alert = useAlert() //const [update, setUpdate] = React.useState("|") const [defaultConfigSet, setDefaultConfigSet] = React.useState( @@ -312,11 +313,31 @@ const AuthenticationOauth2 = (props) => { const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`; const workflowId = workflow !== undefined ? workflow.id : ""; var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`; + + + // This is to make sure authorization can be handled WITHOUT being logged in, + // kind of making it act like an api key + // https://shuffler.io/authorization -> 3rd party integration auth + const urlParams = new URLSearchParams(window.location.search); + const userAuth = urlParams.get("authorization"); + if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) { + console.log("Adding authorization from user side") + state += `%26authorization%3d${userAuth}`; + } + + // Check for org_id + const orgId = urlParams.get("org_id"); + if (orgId !== undefined && orgId !== null && orgId.length > 0) { + console.log("Adding org_id from user side") + state += `%26org_id%3d${orgId}`; + } + if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) { state += `%26oauth_url%3d${oauth_url}`; console.log("ADDING OAUTH2 URL: ", state); } + if ( authenticationType.refresh_uri !== undefined && authenticationType.refresh_uri !== null && @@ -356,7 +377,7 @@ const AuthenticationOauth2 = (props) => { // How can we get a callback properly realtime? // How can we properly try-catch without breaks on error? try { - var newwin = window.open(url, "", "width=800,height=600"); + var newwin = window.open(url, "", "width=582,height=700"); //console.log(newwin) var open = true; @@ -447,9 +468,8 @@ const AuthenticationOauth2 = (props) => { ] = "false"; } else { alert.info( - "Field " + - selectedApp.authentication.parameters[key].name + - " can't be empty" + "Field " + selectedApp.authentication.parameters[key].name.replace("_basic", "", -1).replace("_", " ", -1) + " can't be empty" + ); return; } diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 2f1b765e..354ec93b 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -1,5 +1,6 @@ import React, { useEffect } from "react"; -import { makeStyles } from "@material-ui/styles"; +import theme from '../theme.jsx'; +import { makeStyles } from "@mui/styles"; import { useTheme } from "@material-ui/core/styles"; import Tooltip from "@material-ui/core/Tooltip"; @@ -9,9 +10,12 @@ import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import { useAlert } from "react-alert"; import IconButton from "@material-ui/core/IconButton"; -import ExpandLessIcon from "@material-ui/icons/ExpandLess"; -import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; -import SaveIcon from "@material-ui/icons/Save"; + +import { + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, + Save as SaveIcon, +} from "@mui/icons-material"; const useStyles = makeStyles({ notchedOutline: { @@ -33,7 +37,6 @@ const OrgHeader = (props) => { handleEditOrg, } = props; - const theme = useTheme(); const alert = useAlert(); const classes = useStyles(); diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index f2691e54..6a508aa6 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -1,8 +1,8 @@ import React, { useEffect } from "react"; -import { makeStyles } from "@material-ui/styles"; -import { useTheme } from "@material-ui/core/styles"; +import { makeStyles } from "@mui/styles"; import { useAlert } from "react-alert"; +import theme from '../theme.jsx'; import { FormControl, @@ -23,12 +23,14 @@ import { Tabs, Tab, Grid, -} from "@material-ui/core"; + IconButton, +} from "@mui/material"; -import IconButton from "@material-ui/core/IconButton"; -import ExpandLessIcon from "@material-ui/icons/ExpandLess"; -import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; -import SaveIcon from "@material-ui/icons/Save"; +import { + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, + Save as SaveIcon, +} from "@mui/icons-material"; const useStyles = makeStyles({ notchedOutline: { @@ -46,7 +48,6 @@ const OrgHeaderexpanded = (props) => { adminTab, } = props; - const theme = useTheme(); const alert = useAlert(); const classes = useStyles(); const defaultBranch = "master"; @@ -699,4 +700,4 @@ const OrgHeaderexpanded = (props) => { ) } -export default OrgHeaderexpanded; \ No newline at end of file +export default OrgHeaderexpanded; diff --git a/frontend/src/components/PaperComponent.jsx b/frontend/src/components/PaperComponent.jsx index 76fb56ef..d1e2dc22 100644 --- a/frontend/src/components/PaperComponent.jsx +++ b/frontend/src/components/PaperComponent.jsx @@ -3,7 +3,7 @@ import React, {useState, useEffect, useLayoutEffect} from 'react'; import Draggable from "react-draggable"; import { Paper -} from "@material-ui/core"; +} from "@mui/material"; const PaperComponent = (props) => { return ( diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx old mode 100644 new mode 100755 index 0ff30f85..45ad7dbd --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1,13 +1,14 @@ import React, { useState, useEffect, useLayoutEffect } from "react"; -import { makeStyles, createStyles } from "@material-ui/core/styles"; +import { makeStyles, createStyles } from "@mui/styles"; +import theme from '../theme.jsx'; + import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths } from "../views/Apps.jsx"; import { sortByKey } from "../views/AngularWorkflow.jsx"; -import { useTheme } from "@material-ui/core/styles"; -import NestedMenuItem from "material-ui-nested-menu-item"; +//import NestedMenuItem from "material-ui-nested-menu-item-v5"; +import { NestedMenuItem } from "mui-nested-menu"; import { useAlert } from "react-alert"; -import theme from '../theme.jsx'; import { ButtonGroup, @@ -43,11 +44,8 @@ import { CircularProgress, Switch, Fade, -} from "@material-ui/core"; - -import { Autocomplete -} from "@material-ui/lab"; +} from "@mui/material"; import { HelpOutline as HelpOutlineIcon, @@ -172,7 +170,6 @@ const ParsedAction = (props) => { //setExpansionModalOpen, } = props; - //const theme = useTheme(); const classes = useStyles(); const alert = useAlert() @@ -1595,6 +1592,7 @@ const ParsedAction = (props) => { maxWidth: "95%", fontSize: "1em", }, + disableUnderline: true, endAdornment: hideExtraTypes ? null : ( @@ -2958,6 +2956,7 @@ const ParsedAction = (props) => { style={theme.palette.textFieldStyle} InputProps={{ style: theme.palette.innerTextfieldStyle, + disableUnderline: true, }} fullWidth color="primary" @@ -3177,6 +3176,7 @@ const ParsedAction = (props) => { }} InputProps={{ style: theme.palette.innerTextfieldStyle, + disableUnderline: true, }} placeholder={selectedAction.execution_delay} defaultValue={selectedAction.execution_delay} @@ -3492,7 +3492,7 @@ const ParsedAction = (props) => { ) }} - options={selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))} + options={selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))} ListboxProps={{ style: { backgroundColor: theme.palette.inputColor, diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 88bea907..6e0a872e 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -9,7 +9,7 @@ import { Grid, Card, Switch, -} from "@material-ui/core"; +} from "@mui/material"; import Priority from "../components/Priority.jsx"; import { useAlert } from "react-alert"; diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 0123b37d..8d571d79 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -9,7 +9,7 @@ import { Button, Grid, Card, -} from "@material-ui/core"; +} from "@mui/material"; // import magic wand icon from material ui icons import { diff --git a/frontend/src/components/RenderCytoscape.js b/frontend/src/components/RenderCytoscape.js old mode 100644 new mode 100755 diff --git a/frontend/src/components/ScrollToTop.jsx b/frontend/src/components/ScrollToTop.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/components/Searchfield.js b/frontend/src/components/Searchfield.js deleted file mode 100644 index 43fa7934..00000000 --- a/frontend/src/components/Searchfield.js +++ /dev/null @@ -1,648 +0,0 @@ -import React, {useState, useEffect, useRef} from 'react'; - -import { useNavigate, Link, useParams } from "react-router-dom"; -import { useTheme } from '@material-ui/core/styles'; -import SearchIcon from '@material-ui/icons/Search'; - -import { - Chip, - IconButton, - TextField, - InputAdornment, - List, - Card, - ListItem, - ListItemAvatar, - ListItemText, - Avatar, - Typography, - Tooltip, -} from '@material-ui/core'; - -import { - AvatarGroup, -} from "@mui/material" - -import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons' - -import algoliasearch from 'algoliasearch/lite'; -import aa from 'search-insights' -import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; -//import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; - -// https://www.algolia.com/doc/api-reference/widgets/search-box/react/ -const chipStyle = { - backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white", -} - -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") -const SearchField = props => { - const { serverside, userdata } = props - - const theme = useTheme(); - let navigate = useNavigate(); - const borderRadius = 3 - const node = useRef() - const [searchOpen, setSearchOpen] = useState(false) - const [oldPath, setOldPath] = useState("") - - if (serverside === true) { - return null - } - - if (window !== undefined && window.location !== undefined && window.location.pathname === "/search") { - return null - } - - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - - if (window.location.pathname !== oldPath) { - setSearchOpen(false) - setOldPath(window.location.pathname) - } - - //useEffect(() => { - // if (searchOpen) { - // var tarfield = document.getElementById("shuffle_search_field") - // tarfield.focus() - // } - //}, searchOpen) - - const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => { - - /* - endAdornment: ( - { - event.preventDefault() - }}> - { - setSearchOpen(false) - }} /> - - ), - */ - - return ( -
) diff --git a/frontend/src/components/SuggestedWorkflows.jsx b/frontend/src/components/SuggestedWorkflows.jsx index 1b9148f1..1f01529a 100644 --- a/frontend/src/components/SuggestedWorkflows.jsx +++ b/frontend/src/components/SuggestedWorkflows.jsx @@ -13,7 +13,7 @@ import { CircularProgress, Tooltip, Dialog, -} from "@material-ui/core"; +} from "@mui/material"; import { Close as CloseIcon, diff --git a/frontend/src/components/UsecaseSearch.jsx b/frontend/src/components/UsecaseSearch.jsx index 03fd619f..ba8a98a1 100644 --- a/frontend/src/components/UsecaseSearch.jsx +++ b/frontend/src/components/UsecaseSearch.jsx @@ -25,7 +25,7 @@ import { CircularProgress, Tooltip, Divider, -} from "@material-ui/core"; +} from "@mui/material"; const defaultValue = {"id": "", "name": "Build your own", "source": {"text": "No trigger selected", "error": ""}, diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index da66fb08..b8966a74 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from "react"; import ReactGA from 'react-ga4'; -import Button from "@material-ui/core/Button"; import Checkbox from '@mui/material/Checkbox'; import AliceCarousel from 'react-alice-carousel'; @@ -16,6 +15,7 @@ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import theme from '../theme.jsx'; import { + Button, Fade, IconButton, FormGroup, @@ -37,7 +37,7 @@ import { Tooltip, Chip, ButtonGroup, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; import { useNavigate, Link } from "react-router-dom"; diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index c086790d..e4542e06 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -1,9 +1,9 @@ import React, { useEffect, useState } from 'react'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; +import theme from '../theme.jsx'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; import algoliasearch from 'algoliasearch/lite'; import { InstantSearch, Configure, connectSearchBox, connectHits } from 'react-instantsearch-dom'; @@ -18,7 +18,7 @@ import { Tooltip, Zoom, Chip, -} from '@material-ui/core'; +} from '@mui/material'; import WorkflowPaper from "../components/WorkflowPaper.jsx" import WorkflowPaperNew from "../components/WorkflowPaperNew.jsx" @@ -33,7 +33,6 @@ const AppGrid = props => { const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); diff --git a/frontend/src/components/WorkflowPaper.jsx b/frontend/src/components/WorkflowPaper.jsx index 516756d2..e07daca6 100644 --- a/frontend/src/components/WorkflowPaper.jsx +++ b/frontend/src/components/WorkflowPaper.jsx @@ -8,7 +8,7 @@ import { Avatar, Grid, Tooltip, -} from "@material-ui/core"; +} from "@mui/material"; import { AvatarGroup, @@ -19,7 +19,7 @@ import { Edit as EditIcon, BubbleChart as BubbleChartIcon, MoreVert as MoreVertIcon, -} from '@material-ui/icons'; +} from '@mui/icons-material'; import { useNavigate, Link, useParams } from "react-router-dom"; diff --git a/frontend/src/components/WorkflowPaperNew.jsx b/frontend/src/components/WorkflowPaperNew.jsx index 3234bdee..9d59c2a0 100644 --- a/frontend/src/components/WorkflowPaperNew.jsx +++ b/frontend/src/components/WorkflowPaperNew.jsx @@ -9,7 +9,7 @@ import { Grid, Tooltip, Button, -} from "@material-ui/core"; +} from "@mui/material"; import { AvatarGroup, @@ -20,7 +20,7 @@ import { Edit as EditIcon, BubbleChart as BubbleChartIcon, MoreVert as MoreVertIcon, -} from '@material-ui/icons'; +} from '@mui/icons-material'; import { useNavigate, Link, useParams } from "react-router-dom"; diff --git a/frontend/src/components/Workflowsearch.jsx b/frontend/src/components/Workflowsearch.jsx index 152fdd83..267face5 100644 --- a/frontend/src/components/Workflowsearch.jsx +++ b/frontend/src/components/Workflowsearch.jsx @@ -1,14 +1,14 @@ import React, { useState, useEffect } from 'react'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; +import theme from '../theme.jsx'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; //import algoliasearch from 'algoliasearch/lite'; import algoliasearch from 'algoliasearch'; import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom'; -import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@material-ui/core'; +import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@mui/material'; const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const WorkflowSearch = props => { @@ -16,7 +16,6 @@ const WorkflowSearch = props => { 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(""); diff --git a/frontend/src/components/history.jsx b/frontend/src/components/history.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/css/font1.woff2 b/frontend/src/css/font1.woff2 old mode 100644 new mode 100755 diff --git a/frontend/src/css/font2.woff2 b/frontend/src/css/font2.woff2 old mode 100644 new mode 100755 diff --git a/frontend/src/css/font3.woff2 b/frontend/src/css/font3.woff2 old mode 100644 new mode 100755 diff --git a/frontend/src/css/font4.woff2 b/frontend/src/css/font4.woff2 old mode 100644 new mode 100755 diff --git a/frontend/src/css/font5.woff2 b/frontend/src/css/font5.woff2 old mode 100644 new mode 100755 diff --git a/frontend/src/css/nunito.css b/frontend/src/css/nunito.css old mode 100644 new mode 100755 diff --git a/frontend/src/defaultCytoscapeStyle.js b/frontend/src/defaultCytoscapeStyle.js old mode 100644 new mode 100755 diff --git a/frontend/src/index.css b/frontend/src/index.css old mode 100644 new mode 100755 diff --git a/frontend/src/index.js b/frontend/src/index.js old mode 100644 new mode 100755 index 781cb802..338dbe71 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,12 +1,16 @@ import React from "react"; -import ReactDOM from "react-dom"; -import "./index.css"; +import { createRoot } from "react-dom/client"; import App from "./App"; -import * as serviceWorker from "./serviceWorker"; -ReactDOM.render(, document.getElementById("root")); +//import "./index.css"; +//import reportWebVitals from "./reportWebVitals"; -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: http://bit.ly/CRA-PWA -serviceWorker.unregister(); +const rootElement = document.getElementById("root"); +const root = createRoot(rootElement); +root.render( + + + +); + +//reportWebVitals(); diff --git a/frontend/src/serviceWorker.js b/frontend/src/serviceWorker.js old mode 100644 new mode 100755 diff --git a/frontend/src/theme.js b/frontend/src/theme.js old mode 100644 new mode 100755 index ad6ca44e..051cc846 --- a/frontend/src/theme.js +++ b/frontend/src/theme.js @@ -1,7 +1,7 @@ import React from "react"; -import { createMuiTheme } from "@material-ui/core/styles"; +import { createTheme } from "@mui/material/styles"; -const theme = createMuiTheme({ +const theme = createTheme({ palette: { primary: { main: "#f85a3e", diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index de75558c..9f1fa983 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -1,7 +1,7 @@ import React from "react"; -import { createMuiTheme } from "@material-ui/core/styles"; +import { createTheme } from "@mui/material/styles"; -const theme = createMuiTheme({ +const theme = createTheme({ palette: { primary: { main: "#F86743", diff --git a/frontend/src/views/About.jsx b/frontend/src/views/About.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx old mode 100644 new mode 100755 index b9ad4c8d..8361de43 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1,15 +1,13 @@ import React, { useState, useEffect } from "react"; -import { makeStyles } from "@material-ui/styles"; +import { makeStyles } from "@mui/styles"; import { useNavigate, Link } from "react-router-dom"; import countries from "../components/Countries.jsx"; import CodeEditor from "../components/ShuffleCodeEditor.jsx"; import getLocalCodeData from "../components/ShuffleCodeEditor.jsx"; import CacheView from "../components/CacheView.jsx"; import theme from "../theme.jsx"; -import AddIcon from "@mui/icons-material/Add"; -import ClearIcon from '@mui/icons-material/Clear'; -import StorageIcon from '@mui/icons-material/Storage'; + //import ToggleButton from '@mui/material/ToggleButton'; import { FormControl, @@ -46,7 +44,7 @@ import { CircularProgress, Box, InputAdornment, -} from "@material-ui/core"; +} from "@mui/material"; import { Autocomplete } from "@mui/material"; @@ -57,7 +55,7 @@ import { OpenInNew as OpenInNewIcon, CloudDownload as CloudDownloadIcon, Description as DescriptionIcon, - Polymer as PolymerIcon, + Code as CodeIcon, CheckCircle as CheckCircleIcon, Close as CloseIcon, Apps as AppsIcon, @@ -66,13 +64,15 @@ import { Cached as CachedIcon, AccessibilityNew as AccessibilityNewIcon, Lock as LockIcon, - Eco as EcoIcon, Schedule as ScheduleIcon, Cloud as CloudIcon, Business as BusinessIcon, - Visibility as VisibilityIcon, - VisibilityOff as VisibilityOffIcon, -} from "@material-ui/icons"; + Visibility as VisibilityIcon, + VisibilityOff as VisibilityOffIcon, + Add as AddIcon, + Clear as ClearIcon, + Storage as StorageIcon, +} from "@mui/icons-material"; import { useAlert } from "react-alert"; import Dropzone from "../components/Dropzone.jsx"; @@ -182,6 +182,11 @@ const Admin = (props) => { const [billingInfo, setBillingInfo] = React.useState({}); const [selectedStatus, setSelectedStatus] = React.useState([]); + useEffect(() => { + getUsers() + }, []); + + useEffect(() => { if (isDropzone) { //redirectOpenApi(); @@ -443,17 +448,17 @@ const Admin = (props) => { // Get drift username from userdata.username before @ in email const username = userdata.username.substring(0, userdata.username.indexOf("@")) - var body = `Hey,%0D%0AI saw you trying to use Shuffle, and thought we may be able to help. Right now, it looks like you have ${workflow_amount} workflows made, but I'm not sure if you're getting the most out of Shuffle.%0D%0A%0D%0AIf you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A + var body = `Hey,%0D%0A%0D%0AI saw you trying to use Shuffle, and thought we may be able to help. Right now, it looks like you have ${workflow_amount} workflows made, but it still doesn't look like you are getting the most out of Shuffle. If you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A Some of the things we can help with:%0D%0A ${your_apps} -- Properly authenticating and custom building apps%0D%0A +- Configuring and authenticating your apps%0D%0A ${usecases} -- Creating special usecases%0D%0A%0D%0A +- Creating special usecases and apps%0D%0A%0D%0A Let me know if you're interested, or set up a call here: https://drift.me/${username}` - return `mailto:${admins}?subject=${subject}&body=${body}` + return `mailto:${admins}?bcc=frikky@shuffler.io&subject=${subject}&body=${body}` } const deleteAuthentication = (data) => { @@ -987,6 +992,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user leads.push("student") } + if (responseJson.lead_info.internal) { + leads.push("internal") + } + setSelectedStatus(leads) } @@ -1485,10 +1494,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }); }; - useEffect(() => { - getUsers() - }, []); - const getSettings = () => { fetch(globalUrl + "/api/v1/getsettings", { method: "GET", @@ -2186,7 +2191,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user primary: "Workflows", secondary: "", active: true, - icon: , + icon: , }, { primary: "Apps", @@ -2386,7 +2391,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user renderValue={(selected) => selected.join(', ')} MenuProps={MenuProps} > - {["contacted", "lead", "pov", "demo done", "customer", "student", ].map((name) => ( + {["contacted", "lead", "pov", "demo done", "customer", "student", "internal"].map((name) => ( -1} /> @@ -2755,7 +2760,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user item.usage === null ? 0 : item.usage, data_collection: "None", active: item.active, - icon: , + icon: , }; return ( @@ -3532,8 +3537,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user

App Authentication

- Control the authentication options for individual apps. PS: Actions - performed here can be destructive! + Control the authentication options for individual apps.   - Learn more about authentication + Learn more about App Authentication
- + Environments /> diff --git a/frontend/src/views/AdminSetup.jsx b/frontend/src/views/AdminSetup.jsx old mode 100644 new mode 100755 index df2355bc..f8f3d747 --- a/frontend/src/views/AdminSetup.jsx +++ b/frontend/src/views/AdminSetup.jsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-multi-comp */ import React, { useState } from "react"; -import { makeStyles } from "@material-ui/styles"; +import { makeStyles } from "@mui/styles"; import { CircularProgress, @@ -8,7 +8,7 @@ import { Button, Paper, Typography, -} from "@material-ui/core"; +} from "@mui/material"; const bodyDivStyle = { margin: "auto", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx old mode 100644 new mode 100755 index ca7181be..e45902e5 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1,19 +1,19 @@ /* eslint-disable react/no-multi-comp */ import React, { useState, useEffect, useLayoutEffect } from "react"; import ReactDOM from "react-dom" +import theme from '../theme.jsx'; import { useInterval } from "react-powerhooks"; -import { makeStyles, useTheme } from "@material-ui/core/styles"; +import { makeStyles, } from "@mui/styles"; import { v4 as uuidv4 } from "uuid"; import { useNavigate, Link, useParams } from "react-router-dom"; // import { Prompt } from "react-router"; // FIXME import { useBeforeunload } from "react-beforeunload"; import ReactJson from "react-json-view"; -import NestedMenuItem from "material-ui-nested-menu-item"; +import { NestedMenuItem } from "mui-nested-menu" import ReactMarkdown from "react-markdown"; import { useAlert } from "react-alert"; -import theme from '../theme.jsx'; import { isMobile } from "react-device-detect" import aa from 'search-insights' import Drift from "react-driftjs"; @@ -67,7 +67,8 @@ import { ListItemText, ListItemAvatar, Badge, -} from "@material-ui/core"; + Autocomplete, +} from "@mui/material"; import { AvatarGroup, @@ -87,7 +88,7 @@ import { ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, - Polymer as PolymerIcon, + Code as CodeIcon, FormatListNumbered as FormatListNumberedIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, @@ -108,17 +109,13 @@ import { AddComment as AddCommentIcon, Edit as EditIcon, Send as SendIcon, -} from "@material-ui/icons"; - -import { Preview as PreviewIcon, ContentCopy as ContentCopyIcon, Circle as CircleIcon, SquareFoot as SquareFootIcon, AutoFixHigh as AutoFixHighIcon, -} from '@mui/icons-material'; +} from "@mui/icons-material"; -import Autocomplete from "@material-ui/lab/Autocomplete"; import * as cytoscape from "cytoscape"; import * as edgehandles from "cytoscape-edgehandles"; @@ -402,8 +399,6 @@ const AngularWorkflow = (defaultprops) => { props.match = {} props.match.params = params - //const theme = useTheme(); - var to_be_copied = ""; const [firstrequest, setFirstrequest] = React.useState(true); @@ -4638,7 +4633,8 @@ const AngularWorkflow = (defaultprops) => { }; const addSuggestionButtons = (nodedata, event) => { - //console.log("In suggestion buttons: ", nodedata, ". This is not enabled yet. Backend needs further work.") + console.log("In suggestion buttons: ", nodedata, ". This is not enabled yet. Backend needs further work.") + return // Skipping add for now. Should Re-enable // Add a button for autocompletion based on input @@ -4677,8 +4673,6 @@ const AngularWorkflow = (defaultprops) => { cy.add(decoratorNode); } - - return //setWorkflowRecommendations(responseJson.actions) if (workflowRecommendations.length === 0) { @@ -4999,6 +4993,8 @@ const AngularWorkflow = (defaultprops) => { const edgeData = event.target.data(); if (edgeData.decorator === true) { return; + + } const cytoscapeElement = document.getElementById("cytoscape_view") @@ -12163,7 +12159,7 @@ const AngularWorkflow = (defaultprops) => { margin: "0px 0px 0px 0px", }} > - + Workflows @@ -14561,9 +14557,7 @@ const AngularWorkflow = (defaultprops) => { ? "red" : yellow; - const validate = !codeModalOpen - ? "" - : validateJson(selectedResult.result.trim()); + const validate = ! codeModalOpen? "" : validateJson(selectedResult.result.trim()); if (validate.valid && typeof validate.result === "string") { validate.result = JSON.parse(validate.result); @@ -14573,12 +14567,17 @@ const AngularWorkflow = (defaultprops) => { const [open, setOpen] = React.useState(false) const showVariable = data.value.length < 60 + // Check if it's valid JSON + const checked = validateJson(data.value.trim()) + return (
- {data.value.length > 60 ? + {data.value.length > 60 || checked.valid ? { variant="body1" style={{}} > - {data.name} {showVariable ? data.value : null} + {data.name} + {checked.valid ? + + : null} + {showVariable ? data.value : null} : @@ -14605,15 +14613,25 @@ const AngularWorkflow = (defaultprops) => { } {open ? - - {data.value} - + checked.valid ? + + : + + {data.value} + : null}
) @@ -15071,10 +15089,10 @@ const AngularWorkflow = (defaultprops) => { > - Execution Variable + Runtime Variable - Execution Variables are TEMPORARY variables that you can only be set + Runtime Variables are TEMPORARY variables that you can only be set and used during execution. Learn more{" "} { }, }} margin="dense" - label="Name" + label="Name" fullWidth defaultValue={newVariableName} /> diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx old mode 100644 new mode 100755 index b040bb99..eab88472 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; -import { makeStyles } from "@material-ui/styles"; -import { useTheme } from "@material-ui/core/styles"; +import { makeStyles } from "@mui/styles"; import { BrowserView, MobileView } from "react-device-detect"; +import theme from '../theme.jsx'; import { Paper, @@ -22,7 +22,7 @@ import { Breadcrumbs, CircularProgress, Chip, -} from "@material-ui/core"; +} from "@mui/material"; import { LockOpen as LockOpenIcon, @@ -39,11 +39,8 @@ import { ZoomInOutlined as ZoomInOutlinedIcon, ZoomOutOutlined as ZoomOutOutlinedIcon, Loop as LoopIcon, -} from "@material-ui/icons"; - -import { AddPhotoAlternate as AddPhotoAlternateIcon, -} from '@mui/icons-material'; +} from "@mui/icons-material"; import { v4 as uuidv4 } from "uuid"; import { Link, useParams } from "react-router-dom"; @@ -354,7 +351,6 @@ const AppCreator = (defaultprops) => { const { globalUrl, isLoaded } = defaultprops; const classes = useStyles(); const alert = useAlert(); - const theme = useTheme(); const params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx old mode 100644 new mode 100755 index 44c3e594..8086e1e7 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -1,6 +1,7 @@ import React, { useEffect } from "react"; import { useInterval } from "react-powerhooks"; +import theme from '../theme.jsx'; import { IconButton, @@ -32,7 +33,7 @@ import { ListItemAvatar, ListItemText, Avatar, -} from "@material-ui/core"; +} from "@mui/material"; import { LockOpen as LockOpenIcon, @@ -46,14 +47,13 @@ import { Search as SearchIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import { ForkRight as ForkRightIcon, } from '@mui/icons-material'; import aa from 'search-insights' -import { useTheme } from "@material-ui/core/styles"; import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; import algoliasearch from 'algoliasearch/lite'; @@ -271,7 +271,6 @@ const Apps = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata } = props; //const [workflows, setWorkflows] = React.useState([]); - const theme = useTheme(); const baseRepository = "https://github.com/frikky/shuffle-apps"; const alert = useAlert(); let navigate = useNavigate(); diff --git a/frontend/src/views/Contact.jsx b/frontend/src/views/Contact.jsx new file mode 100755 index 00000000..afd609a4 --- /dev/null +++ b/frontend/src/views/Contact.jsx @@ -0,0 +1,343 @@ +import React, { useState } from 'react'; +import { BrowserView, MobileView } from "react-device-detect"; +import theme from '../theme.jsx'; + +import Paper from '@material-ui/core/Paper'; +import Button from '@material-ui/core/Button'; + +import TextField from '@material-ui/core/TextField'; + + +const bodyDivStyle = { + margin: "auto", + textAlign: "center", + width: "900px", +} + + +// Should be different if logged in :| +const Contact = (props) => { + const { globalUrl, isLoaded } = props; + + const boxStyle = { + flex: "1", + marginLeft: "10px", + marginRight: "10px", + paddingLeft: "30px", + paddingRight: "30px", + paddingBottom: "30px", + paddingTop: "30px", + backgroundColor: theme.palette.surfaceColor, + display: "flex", + flexDirection: "column" + } + + const bodyTextStyle = { + color: "#ffffff", + } + + const [firstname, setFirstname] = useState(""); + const [lastname, setLastname] = useState(""); + const [title, setTitle] = useState(""); + const [companyname, setCompanyname] = useState(""); + const [email, setEmail] = useState(""); + const [phone, setPhone] = useState(""); + const [message, setMessage] = useState(""); + + const [formMessage, setFormMessage] = useState(""); + + const submitContact = () => { + const data = { + "firstname": firstname, + "lastname": lastname, + "title": title, + "companyname": companyname, + "email": email, + "phone": phone, + "message": message, + } + console.log(data) + + fetch(globalUrl + "/api/v1/contact", { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(response => { + if (response.success === true) { + setFormMessage(response.message) + } else { + setFormMessage("Something went wrong. Please contact frikky@shuffler.io.") + } + console.log(response) + }) + .catch(error => { + console.log(error) + }); + } + + // Random names for type & autoComplete. Didn't research :^) + const landingpageDataBrowser = +
+
+

Contact us

+

Lets talk!

+
+
+ +

Contact Details

+
+ setFirstname(e.target.value)} + /> + setLastname(e.target.value)} + /> +
+
+ setTitle(e.target.value)} + /> + setCompanyname(e.target.value)} + /> +
+
+ setEmail(e.target.value)} + /> + setPhone(e.target.value)} + /> +
+
+

Message

+
+
+ setMessage(e.target.value)} + /> +
+ +

{formMessage}

+
+
+
+ + const landingpageDataMobile = +
+
+

Contact us

+

Lets talk!

+
+
+ +

Contact Details

+
+ setFirstname(e.target.value)} + /> +
+
+ setEmail(e.target.value)} + /> +
+
+

Message

+
+
+ setMessage(e.target.value)} + /> +
+ +

{formMessage}

+
+
+
+ + + const loadedCheck = isLoaded ? +
+ +
{landingpageDataBrowser}
+
+ + {landingpageDataMobile} + +
+ : +
+
+ + return ( +
+ {loadedCheck} +
+ ) +} +export default Contact; diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx old mode 100644 new mode 100755 index 15549a36..dfe028fa --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import { useInterval } from "react-powerhooks"; import AppFramework from "../components/AppFramework.jsx"; -import { makeStyles, useTheme } from "@material-ui/core/styles"; +import { makeStyles, useTheme } from "@mui/styles"; // nodejs library that concatenates classes import classNames from "classnames"; import theme from '../theme.jsx'; @@ -10,9 +10,9 @@ import { useNavigate, Link, useParams } from "react-router-dom"; // react plugin used to create charts //import { Line, Bar } from "react-chartjs-2"; import { useAlert } from "react-alert"; -import Autocomplete from "@material-ui/lab/Autocomplete"; import { + Autocomplete, Tooltip, TextField, IconButton, @@ -22,7 +22,7 @@ import { Paper, Chip, Checkbox, -} from "@material-ui/core"; +} from "@mui/material"; import { Close as CloseIcon, @@ -33,7 +33,7 @@ import { CheckBox as CheckBoxIcon, CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon, OpenInNew as OpenInNewIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import WorkflowPaper from "../components/WorkflowPaper.jsx" import { removeParam } from "../views/AngularWorkflow.jsx" diff --git a/frontend/src/views/DashboardViews.jsx b/frontend/src/views/DashboardViews.jsx index 19c678fa..a23a3566 100644 --- a/frontend/src/views/DashboardViews.jsx +++ b/frontend/src/views/DashboardViews.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { useInterval } from "react-powerhooks"; -import { makeStyles, useTheme } from "@material-ui/core/styles"; +import { makeStyles, useTheme } from "@mui/styles"; // nodejs library that concatenates classes import classNames from "classnames"; import theme from '../theme.jsx'; @@ -9,10 +9,10 @@ import { useNavigate, Link, useParams } from "react-router-dom"; // react plugin used to create charts //import { Line, Bar } from "react-chartjs-2"; import { useAlert } from "react-alert"; -import Autocomplete from "@material-ui/lab/Autocomplete"; import Draggable from "react-draggable"; import { + Autocomplete, Tooltip, TextField, IconButton, @@ -22,7 +22,7 @@ import { Paper, Chip, Checkbox, -} from "@material-ui/core"; +} from "@mui/material"; import { Close as CloseIcon, @@ -33,7 +33,7 @@ import { CheckBox as CheckBoxIcon, CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon, OpenInNew as OpenInNewIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import WorkflowPaper from "../components/WorkflowPaper.jsx" import { removeParam } from "../views/AngularWorkflow.jsx" diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx old mode 100644 new mode 100755 index e0d974f5..ea5354ea --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -1,10 +1,10 @@ import React, { useEffect, useState } from "react"; -import { useTheme } from "@material-ui/core/styles"; import ReactMarkdown from "react-markdown"; import { BrowserView, MobileView } from "react-device-detect"; import { useParams, useNavigate, Link } from "react-router-dom"; import { isMobile } from "react-device-detect"; +import theme from '../theme.jsx'; import { Grid, @@ -18,12 +18,12 @@ import { Typography, Paper, List, -} from "@material-ui/core"; +} from "@mui/material"; import { Link as LinkIcon, Edit as EditIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const Body = { //maxWidth: 1000, @@ -57,7 +57,6 @@ const Docs = (defaultprops) => { const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; let navigate = useNavigate(); - const theme = useTheme(); // Quickfix for react router 5 -> 6 const params = useParams(); diff --git a/frontend/src/views/EditSchedule.jsx b/frontend/src/views/EditSchedule.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/EditWebhook.jsx b/frontend/src/views/EditWebhook.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/EditWorkflow.jsx b/frontend/src/views/EditWorkflow.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/Faq.jsx b/frontend/src/views/Faq.jsx index 42d24261..146ceb68 100644 --- a/frontend/src/views/Faq.jsx +++ b/frontend/src/views/Faq.jsx @@ -2,8 +2,8 @@ import React, {useState} from 'react'; import {isMobile} from "react-device-detect"; import {Link} from 'react-router-dom'; -import {Divider, List, ListItem, ListItemText, Card, CardContent, Grid, Typography, Button, ButtonGroup, FormControl, Dialog, DialogTitle, DialogActions, DialogContent, Tooltip} from '@material-ui/core'; -import {ExpandMore as ExpandMoreIcon, ExpandLess as ExpandLessIcon} from '@material-ui/icons'; +import {Divider, List, ListItem, ListItemText, Card, CardContent, Grid, Typography, Button, ButtonGroup, FormControl, Dialog, DialogTitle, DialogActions, DialogContent, Tooltip} from '@mui/material'; +import {ExpandMore as ExpandMoreIcon, ExpandLess as ExpandLessIcon} from '@mui/icons-material'; const hrefStyle = { textDecoration: "none", @@ -255,4 +255,4 @@ const Faq = (props) => { ) } -export default Faq; \ No newline at end of file +export default Faq; diff --git a/frontend/src/views/ForgotPassword.jsx b/frontend/src/views/ForgotPassword.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/ForgotPasswordLink.jsx b/frontend/src/views/ForgotPasswordLink.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/FrameworkWrapper.jsx b/frontend/src/views/FrameworkWrapper.jsx index 2d96e03c..0daf0a3d 100644 --- a/frontend/src/views/FrameworkWrapper.jsx +++ b/frontend/src/views/FrameworkWrapper.jsx @@ -8,7 +8,7 @@ import theme from '../theme.jsx'; import { Button, -} from "@material-ui/core"; +} from "@mui/material"; const Framework = (props) => { const {globalUrl, isLoaded, isLoggedIn, showOptions, selectedOption, rolling, } = props; diff --git a/frontend/src/views/GettingStarted.jsx b/frontend/src/views/GettingStarted.jsx index 87dd1be0..9324e159 100644 --- a/frontend/src/views/GettingStarted.jsx +++ b/frontend/src/views/GettingStarted.jsx @@ -1,9 +1,9 @@ import React, { useEffect, useContext } from "react"; -import { makeStyles } from "@material-ui/core/styles"; -import { useTheme } from "@material-ui/core/styles"; +import { makeStyles } from "@mui/styles"; import ReactGA from 'react-ga4'; import SecurityFramework from '../components/SecurityFramework.jsx'; +import theme from '../theme.jsx'; import { Badge, @@ -28,7 +28,7 @@ import { DialogTitle, DialogActions, DialogContent, -} from "@material-ui/core"; +} from "@mui/material"; import { Check as CheckIcon, @@ -58,7 +58,6 @@ import { CloudDownload as CloudDownloadIcon, } from "@mui/icons-material"; -import NestedMenuItem from "material-ui-nested-menu-item"; //import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; //https://next.material-ui.com/components/material-icons/ @@ -126,7 +125,6 @@ const GettingStarted = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata } = props; document.title = "Getting Started with Shuffle"; - const theme = useTheme(); const alert = useAlert(); const classes = useStyles(theme); let navigate = useNavigate(); @@ -1061,13 +1059,6 @@ const GettingStarted = (props) => { {"Duplicate Workflow"}
- {/*= 0} style={{backgroundColor: inputColor, color: "white"}} onClick={() => { - //copyWorkflow(data) - //setOpen(false) - }} key={"duplicate"}> - - {"Copy to Child Org"} - */} { diff --git a/frontend/src/views/HandlePayment.js b/frontend/src/views/HandlePayment.js old mode 100644 new mode 100755 diff --git a/frontend/src/views/HandlePaymentNew.jsx b/frontend/src/views/HandlePaymentNew.jsx index 1aa24cc6..89546b9b 100644 --- a/frontend/src/views/HandlePaymentNew.jsx +++ b/frontend/src/views/HandlePaymentNew.jsx @@ -28,7 +28,7 @@ import { DialogActions, DialogContent, Tooltip -} from '@material-ui/core'; +} from '@mui/material'; import FAQ from "./Faq.jsx"; import Newsletter from "../components/Newsletter.jsx"; import Services from "./Services.jsx"; diff --git a/frontend/src/views/Introduction.jsx b/frontend/src/views/Introduction.jsx old mode 100644 new mode 100755 index bc3e1734..e94295f3 --- a/frontend/src/views/Introduction.jsx +++ b/frontend/src/views/Introduction.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { useTheme } from "@material-ui/core/styles"; import { Link, useParams } from "react-router-dom"; +import theme from '../theme.jsx'; import Grid from "@material-ui/core/Grid"; import Card from "@material-ui/core/Card"; @@ -13,7 +13,6 @@ import Button from "@material-ui/core/Button"; const Workflows = (defaultprops) => { const { globalUrl, isLoggedIn, isLoaded } = defaultprops; - const theme = useTheme(); const params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) props.match = {} diff --git a/frontend/src/views/Landingpage.jsx b/frontend/src/views/Landingpage.jsx old mode 100644 new mode 100755 index ed02060f..a0de1eee --- a/frontend/src/views/Landingpage.jsx +++ b/frontend/src/views/Landingpage.jsx @@ -4,9 +4,11 @@ import Paper from "@material-ui/core/Paper"; import Button from "@material-ui/core/Button"; import Divider from "@material-ui/core/Divider"; import { BrowserView, MobileView } from "react-device-detect"; -import ScheduleIcon from "@material-ui/icons/Schedule"; -import Web from "@material-ui/icons/Web"; -import AccountTree from "@material-ui/icons/AccountTree"; +import { + Schedule as ScheduleIcon, + Web as WebIcon, + AccountTree as AccountTreeIcon, +} from "@mui/icons-material"; const bodyDivStyle = { margin: "auto", diff --git a/frontend/src/views/LandingpageLoggedin.jsx b/frontend/src/views/LandingpageLoggedin.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/LandingpageNew.jsx b/frontend/src/views/LandingpageNew.jsx old mode 100644 new mode 100755 index 252d389c..c859beee --- a/frontend/src/views/LandingpageNew.jsx +++ b/frontend/src/views/LandingpageNew.jsx @@ -11,12 +11,14 @@ import Divider from "@material-ui/core/Divider"; import Grid from "@material-ui/core/Grid"; import { BrowserView, MobileView } from "react-device-detect"; -import ScheduleIcon from "@material-ui/icons/Schedule"; -import Web from "@material-ui/icons/Web"; -import AccountTree from "@material-ui/icons/AccountTree"; -import InfoIcon from "@material-ui/icons/Info"; -import ArrowForwardIcon from "@material-ui/icons/ArrowForward"; -import CreateIcon from "@material-ui/icons/Create"; +import { + Schedule as ScheduleIcon, + Web as WebIcon, + AccountTree as AccountTreeIcon, + Info as InfoIcon, + ArrowForward as ArrowForwardIcon, + Create as CreateIcon, +} from "@mui/icons-material"; const bodyDivStyle = { margin: "auto", @@ -86,7 +88,7 @@ const LandingPage = (props) => { "Simple integrations", "Easily use others' or create your own integration", "/docs/features", - ), @@ -94,7 +96,7 @@ const LandingPage = (props) => { "Workflows", "Access the power of automation within minutes, whether its on premise or in the cloud", "/docs/features", - ), diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx old mode 100644 new mode 100755 index 4cb4b27c..1a0a4090 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -1,7 +1,8 @@ /* eslint-disable react/no-multi-comp */ import React, { useState, useEffect } from "react"; -import { makeStyles } from "@material-ui/styles"; +import { makeStyles } from "@mui/styles"; import { useInterval } from "react-powerhooks"; +import theme from '../theme.jsx'; import { CircularProgress, @@ -9,9 +10,8 @@ import { Button, Paper, Typography, -} from "@material-ui/core"; +} from "@mui/material"; -import { useTheme } from "@material-ui/core/styles"; import { useNavigate } from "react-router-dom"; const hrefStyle = { @@ -42,7 +42,6 @@ const LoginDialog = (props) => { checkLogin, } = props; - const theme = useTheme(); let navigate = useNavigate(); const classes = useStyles(); diff --git a/frontend/src/views/MyView.jsx b/frontend/src/views/MyView.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/Oauth2.jsx b/frontend/src/views/Oauth2.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/Post.jsx b/frontend/src/views/Post.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/PrivacyPolicy.jsx b/frontend/src/views/PrivacyPolicy.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/RegisterLink.jsx b/frontend/src/views/RegisterLink.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/RegisterPage.jsx b/frontend/src/views/RegisterPage.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/RunWorkflow.jsx b/frontend/src/views/RunWorkflow.jsx index ebc1cad6..51c31408 100644 --- a/frontend/src/views/RunWorkflow.jsx +++ b/frontend/src/views/RunWorkflow.jsx @@ -3,7 +3,7 @@ import React, {useState, useEffect} from 'react'; import ReactDOM from "react-dom" import { useInterval } from "react-powerhooks"; -import { makeStyles } from '@material-ui/styles'; +import { makeStyles } from '@mui/material/styles'; import { useNavigate, Link, useParams } from "react-router-dom"; import {isMobile} from "react-device-detect"; import theme from '../theme.jsx'; @@ -20,7 +20,7 @@ import { Paper, Typography, Divider, -} from '@material-ui/core'; +} from '@mui/material'; import { Preview as PreviewIcon, diff --git a/frontend/src/views/Schedules.jsx b/frontend/src/views/Schedules.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/Search.jsx b/frontend/src/views/Search.jsx index 942a1ec9..ffa2061f 100644 --- a/frontend/src/views/Search.jsx +++ b/frontend/src/views/Search.jsx @@ -11,14 +11,14 @@ import { useNavigate } from "react-router-dom"; import { Tabs, Tab, -} from "@material-ui/core"; +} from "@mui/material"; import { Apps as AppsIcon, - Polymer as PolymerIcon, + Code as CodeIcon, EmojiObjects as EmojiObjectsIcon, Description as DescriptionIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const bodyDivStyle = { @@ -137,7 +137,7 @@ const Search = (props) => { /> - Workflows + Workflows /> { const { globalUrl } = props; + var headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + const [firstRequest, setFirstRequest] = useState(true); const [finished, setFinished] = useState(false); const [response, setResponse] = useState(""); const [failed, setFailed] = useState(false); + const [requestHeaders, setRequestHeaders] = useState(headers); if (firstRequest) { setFirstRequest(false); @@ -20,6 +26,8 @@ const SetAuthentication = (props) => { const params = Object.fromEntries(urlSearchParams.entries()); console.log("PARAMS: ", params) + + //const authenticationStore = []; var appAuthData = { label: "", @@ -40,7 +48,6 @@ const SetAuthentication = (props) => { }); } - if (params.session_state !== undefined && params.session_state !== null) { appAuthData.fields.push({ key: "session_state", @@ -83,6 +90,14 @@ const SetAuthentication = (props) => { continue; } + if (query[0] === "org_id") { + headers["Org-Id"] = query[1] + } + + if (query[0] === "authorization") { + headers["Authorization"] = "Bearer " + query[1] + } + if (query[0] === "workflow_id") { appAuthData.reference_workflow = query[1]; } @@ -136,6 +151,8 @@ const SetAuthentication = (props) => { appAuthData.fields.push({ key: "refresh_url", value: query[1] }); } } + + setRequestHeaders(headers) } if (foundScope !== undefined && foundScope !== null && foundScope.length > 0) { @@ -153,15 +170,13 @@ const SetAuthentication = (props) => { setFailed(true) setResponse(`${foundTab}`) } else { + if (externalData.handleExternal) { console.log("RUN EXTERNAL!!: ", externalData) fetch(globalUrl + "/api/v1/triggers/github/register", { method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + headers: requestHeaders, credentials: "include", body: JSON.stringify(externalData), }) @@ -227,23 +242,16 @@ const SetAuthentication = (props) => { fetch(globalUrl + "/api/v1/apps/authentication", { method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + headers: requestHeaders, credentials: "include", body: JSON.stringify(appAuthData), }) .then((response) => { - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; - const tmpView = new URLSearchParams(cursearch).get("state"); - if ( - tmpView !== undefined && - tmpView !== null && - tmpView.length > 0 - ) { - console.log("State to find app name from: ", tmpView) - } + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + const tmpView = new URLSearchParams(cursearch).get("state"); + if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { + console.log("State to find app name from: ", tmpView) + } if (response.status !== 200) { console.log("Status not 200 for oauth2 authentication"); diff --git a/frontend/src/views/SetAuthenticationSSO.jsx b/frontend/src/views/SetAuthenticationSSO.jsx old mode 100644 new mode 100755 index 371eedb8..1b46bacd --- a/frontend/src/views/SetAuthenticationSSO.jsx +++ b/frontend/src/views/SetAuthenticationSSO.jsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -import { Typography, CircularProgress } from "@material-ui/core"; +import { Typography, CircularProgress } from "@mui/material"; const SetAuthentication = (props) => { const { globalUrl } = props; diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx old mode 100644 new mode 100755 index b0a7c038..bbac998b --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; +import theme from '../theme.jsx'; import { Grid, Typography, @@ -8,15 +9,13 @@ import { Button, Divider, TextField, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; -import { useTheme } from "@material-ui/core/styles"; import detectEthereumProvider from "@metamask/detect-provider"; const Settings = (props) => { const { globalUrl, isLoaded, userdata, setUserData } = props; - const theme = useTheme(); const alert = useAlert(); let navigate = useNavigate(); diff --git a/frontend/src/views/TempDashboard.jsx b/frontend/src/views/TempDashboard.jsx index 4d4ab368..a34ac730 100644 --- a/frontend/src/views/TempDashboard.jsx +++ b/frontend/src/views/TempDashboard.jsx @@ -1,7 +1,7 @@ import React from "react"; import { Grid, Container, Divider } from "@mui/material"; -import { makeStyles } from "@material-ui/core/styles"; +import { makeStyles } from "@mui/material/styles"; import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; diff --git a/frontend/src/views/UpdateAuthentication.jsx b/frontend/src/views/UpdateAuthentication.jsx new file mode 100644 index 00000000..22504282 --- /dev/null +++ b/frontend/src/views/UpdateAuthentication.jsx @@ -0,0 +1,172 @@ +import React, { useState, useEffect } from "react"; + +import ReactGA from "react-ga4"; +import { + Typography, + CircularProgress, + Button, +} from "@mui/material"; +import theme from '../theme.jsx'; +import { useAlert } from "react-alert"; + +import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; +import AuthenticationWindow from "../components/AuthenticationWindow.jsx"; +import { base64_decode, appCategories } from "../views/AppCreator.jsx"; + +const SetAuthentication = (props) => { + const { globalUrl, serverside } = props; + + const [app, setApp] = useState({}); + const [isAppLoaded, setIsAppLoaded] = useState(false); + const [loadFail, setLoadFail] = useState(""); + const [appAuthentication, setAppAuthentication] = React.useState([]); + + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const alert = useAlert(); + + const parseIncomingOpenapiData = (data) => { + if (data.app === undefined || data.app === null) { + return + } + + // Should basically always be true if openapi exists too + var parsedBaseapp = "" + try { + parsedBaseapp = base64_decode(data.app) + } catch (e) { + console.log("Failed JSON parsing: ", e) + parsedBaseapp = data + } + + var parsedapp = JSON.parse(parsedBaseapp) + parsedapp.name = parsedapp.name.replaceAll("_", " "); + setApp(parsedapp); + + document.title = parsedapp.name + " App Auth"; + + } + + console.log("App: ", app) + + const getApp = (appid) => { + if (serverside === true) { + return; + } + + fetch(`${globalUrl}/api/v1/apps/${appid}/config`, { + method: "GET", + headers: { + "Content-Type": "application/json", + "Accept": "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + if (isCloud) { + ReactGA.event({ + category: "appauth", + action: `app_not_found`, + label: appid, + }); + } + } else { + if (isCloud) { + ReactGA.event({ + category: "appauth", + action: `app_found`, + label: appid, + }); + } + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false || responseJson.success === undefined) { + alert.error("Failed to get the app. Does it exist?") + setIsAppLoaded(true) + return; + } + + parseIncomingOpenapiData(responseJson); + }) + .catch((error) => { + alert.error("Error in app fetch: " + error.toString()); + }); + }; + + useEffect(() => { + // Find the ID for the app from the "app_id" query + const urlParams = new URLSearchParams(window.location.search); + const appid = urlParams.get("app_id"); + if (appid === null) { + setLoadFail( + + + Failed to load the app. Please contact your provider or support@shuffler.io if this persists + + + + ) + } else { + getApp(appid); + } + }, []); + + // Handle: + // 1. Check for org_id, authentication, and app keys in queries + // 2. Load the app auth info from the orgs' apps + // 3. Help them set info for the app + // Make sure to test both private and public apps + + const appname = app.name !== undefined ? app.name : ""; + + return ( +
+ {loadFail !== "" ? + loadFail + : +
+ + Configure {appname} Authentication + + {app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ? + null + : + app.authentication.type === "oauth2" ? + + : + + } +
+ } +
+ ) +}; + +export default SetAuthentication; diff --git a/frontend/src/views/Webhooks.jsx b/frontend/src/views/Webhooks.jsx old mode 100644 new mode 100755 diff --git a/frontend/src/views/Welcome.jsx b/frontend/src/views/Welcome.jsx index 22c571c9..ebdd5036 100644 --- a/frontend/src/views/Welcome.jsx +++ b/frontend/src/views/Welcome.jsx @@ -1,9 +1,11 @@ import React, { useState, useEffect } from 'react'; import ReactGA from 'react-ga4'; import WelcomeForm2 from "../components/WelcomeForm2.jsx"; -import Stepper from "@material-ui/core/Stepper"; -import Step from "@material-ui/core/Step"; -import StepLabel from "@material-ui/core/StepLabel"; +import { + Stepper, + Step, + StepLabel, +} from '@mui/material'; import AppFramework from "../components/AppFramework.jsx"; import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; import { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx old mode 100644 new mode 100755 index 3bab7185..30982e9d --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useContext } from "react"; import ReactDOM from "react-dom" -import { makeStyles } from "@material-ui/core/styles"; +import { makeStyles } from "@mui/styles"; import { Navigate } from "react-router-dom"; //import { Redirect } from "react-router-dom"; @@ -41,7 +41,7 @@ import { Checkbox, LinearProgress, ListItemText, -} from "@material-ui/core"; +} from "@mui/material" import { AvatarGroup, @@ -79,7 +79,7 @@ import { RadioButtonUnchecked as RadioButtonUncheckedIcon, ArrowLeft as ArrowLeftIcon, ArrowRight as ArrowRightIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; //import NestedMenuItem from "material-ui-nested-menu-item"; //import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; @@ -462,7 +462,21 @@ export const validateJson = (showResult) => { try { var newstr = showResult.replaceAll("'", '"') - //console.log("Try replacements and trimming with new value: ", newstr) + // Basic workarounds for issues with Python Dicts -> JSON + if (newstr.includes(": None")) { + newstr = newstr.replaceAll(": None", ': null') + } + + if (newstr.includes("[\"{") && newstr.includes("}\"]")) { + newstr = newstr.replaceAll("[\"{", '[{') + newstr = newstr.replaceAll("}\"]", '}]') + } + + if (newstr.includes("{\"[") && newstr.includes("]\"}")) { + newstr = newstr.replaceAll("{\"[", '[{') + newstr = newstr.replaceAll("]\"}", '}]') + } + result = JSON.parse(newstr) jsonvalid = true } catch (e) { @@ -3454,7 +3468,7 @@ const Workflows = (props) => {
) : null} - {userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") ? + {userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ?
diff --git a/functions/README.md b/functions/README.md old mode 100644 new mode 100755 diff --git a/functions/extensions/cortex-responders/Shuffle/requirements.txt b/functions/extensions/cortex-responders/Shuffle/requirements.txt old mode 100644 new mode 100755 diff --git a/functions/extensions/cortex-responders/Shuffle/shuffle.json b/functions/extensions/cortex-responders/Shuffle/shuffle.json old mode 100644 new mode 100755 diff --git a/functions/extensions/cortex-responders/Shuffle/shuffle.py b/functions/extensions/cortex-responders/Shuffle/shuffle.py old mode 100644 new mode 100755 diff --git a/functions/extensions/cortex-responders/Shuffle_Webhook/requirements.txt b/functions/extensions/cortex-responders/Shuffle_Webhook/requirements.txt old mode 100644 new mode 100755 diff --git a/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.json b/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.json old mode 100644 new mode 100755 diff --git a/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.py b/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.py old mode 100644 new mode 100755 diff --git a/functions/extensions/elasticsearch/run.sh b/functions/extensions/elasticsearch/run.sh old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/.helmignore b/functions/extensions/k8s/shuffle/.helmignore old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/Chart.yaml b/functions/extensions/k8s/shuffle/Chart.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/NOTES.txt b/functions/extensions/k8s/shuffle/templates/NOTES.txt old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/_helpers.tpl b/functions/extensions/k8s/shuffle/templates/_helpers.tpl old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/deployment.yaml b/functions/extensions/k8s/shuffle/templates/deployment.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/hpa.yaml b/functions/extensions/k8s/shuffle/templates/hpa.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/ingress.yaml b/functions/extensions/k8s/shuffle/templates/ingress.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml b/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml b/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/service.yaml b/functions/extensions/k8s/shuffle/templates/service.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml b/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml b/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/k8s/shuffle/values.yaml b/functions/extensions/k8s/shuffle/values.yaml old mode 100644 new mode 100755 diff --git a/functions/extensions/misp/Dockerfile b/functions/extensions/misp/Dockerfile old mode 100644 new mode 100755 diff --git a/functions/extensions/misp/docker-compose.yml b/functions/extensions/misp/docker-compose.yml old mode 100644 new mode 100755 diff --git a/functions/extensions/misp/requirements.txt b/functions/extensions/misp/requirements.txt old mode 100644 new mode 100755 diff --git a/functions/extensions/misp/sub.py b/functions/extensions/misp/sub.py old mode 100644 new mode 100755 diff --git a/functions/extensions/scripts/disable_disk_check.sh b/functions/extensions/scripts/disable_disk_check.sh old mode 100644 new mode 100755 diff --git a/functions/extensions/wazuh/custom-shuffle b/functions/extensions/wazuh/custom-shuffle old mode 100644 new mode 100755 diff --git a/functions/extensions/wazuh/custom-shuffle.py b/functions/extensions/wazuh/custom-shuffle.py old mode 100644 new mode 100755 diff --git a/functions/extensions/wazuh/ossec.conf b/functions/extensions/wazuh/ossec.conf old mode 100644 new mode 100755 diff --git a/functions/onprem/README.md b/functions/onprem/README.md old mode 100644 new mode 100755 diff --git a/functions/onprem/orborus/Dockerfile b/functions/onprem/orborus/Dockerfile old mode 100644 new mode 100755 diff --git a/functions/onprem/orborus/build.sh b/functions/onprem/orborus/build.sh old mode 100644 new mode 100755 diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod old mode 100644 new mode 100755 diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum old mode 100644 new mode 100755 diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go old mode 100644 new mode 100755 index 1944b2b4..21e42e9e --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -586,7 +586,7 @@ func deployWorker(image string, identifier string, env []string, executionReques hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) - if cleanupEnv == "true" { + if strings.ToLower(cleanupEnv) == "true" { hostConfig.AutoRemove = true } diff --git a/functions/onprem/orborus/run.sh b/functions/onprem/orborus/run.sh old mode 100644 new mode 100755 diff --git a/functions/onprem/worker/Dockerfile b/functions/onprem/worker/Dockerfile old mode 100644 new mode 100755 diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh old mode 100644 new mode 100755 diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod old mode 100644 new mode 100755 diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum old mode 100644 new mode 100755 diff --git a/functions/onprem/worker/run b/functions/onprem/worker/run old mode 100644 new mode 100755 diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go old mode 100644 new mode 100755 diff --git a/shuffle-apps/README.md b/shuffle-apps/README.md old mode 100644 new mode 100755