From fd2996f101d19ee7fe4d26913c3e7301cb5c1863 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:24:10 +0100 Subject: [PATCH 001/200] allow to change service accounts for worker and apps Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/onprem/orborus/orborus.go | 39 +++++++++++++---------------- functions/onprem/worker/worker.go | 13 ++++++---- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 093faf79..2ee3c441 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -75,9 +75,13 @@ var workerVersion = os.Getenv("SHUFFLE_WORKER_VERSION") var newWorkerImage = os.Getenv("SHUFFLE_WORKER_IMAGE") var dockerSwarmBridgeMTU = os.Getenv("SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU") var dockerSwarmBridgeInterface = os.Getenv("SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE") +var maxCPUPercent = 90 + +// Kubernetes settings var isKubernetes = os.Getenv("IS_KUBERNETES") var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") -var maxCPUPercent = 90 +var workerServiceAccountName = os.Getenv("SHUFFLE_WORKER_SERVICE_ACCOUNT_NAME") +var appServiceAccountName = os.Getenv("SHUFFLE_APP_SERVICE_ACCOUNT_NAME") // var baseimagename = "docker.pkg.github.com/shuffle/shuffle" // var baseimagename = "ghcr.io/frikky" @@ -1000,6 +1004,10 @@ func deployK8sWorker(image string, identifier string, env []string) error { env = append(env, fmt.Sprintf("SHUFFLE_USE_GHCR_OVERRIDE_FOR_AUTODEPLOY=%s", os.Getenv("SHUFFLE_USE_GHCR_OVERRIDE_FOR_AUTODEPLOY"))) } + if len(appServiceAccountName) > 0 { + env = append(env, fmt.Sprintf("SHUFFLE_APP_SERVICE_ACCOUNT_NAME=%s", appServiceAccountName)) + } + clientset, _, err := shuffle.GetKubernetesClient() if err != nil { log.Printf("[ERROR] Error getting kubernetes client:", err) @@ -1008,18 +1016,6 @@ func deployK8sWorker(image string, identifier string, env []string) error { //env = append(env, fmt.Sprintf("KUBERNETES_CONFIG=%s", config.String())) - // FIXME: When a service account is used, the account is also mounted in the pod - // The volume mount location is: - // /var/run/secrets/kubernetes.io/serviceaccount - - // Look for if there is a default service account in use - if len(os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) > 0 { - log.Printf("[DEBUG] Using Kubernetes service account %s", os.Getenv("KUBERNETES_SERVICE_ACCOUNT")) - env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_ACCOUNT=%s", os.Getenv("KUBERNETES_SERVICE_ACCOUNT"))) - - // use k8s downward API to find it if we are in a pod - } - // Check if namespace exist as variable. If so, make it if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 && !namespacemade { kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") @@ -1199,7 +1195,8 @@ func deployK8sWorker(image string, identifier string, env []string) error { Containers: []corev1.Container{ containerAttachment, }, - DNSPolicy: corev1.DNSClusterFirst, + DNSPolicy: corev1.DNSClusterFirst, + ServiceAccountName: workerServiceAccountName, }, }, }, @@ -2296,7 +2293,7 @@ func main() { // Manual command = overrides to allow starting of Tenzir from the frontend anyway. os.Setenv("SHUFFLE_SKIP_PIPELINES", "false") - tenzirDisabled = false + tenzirDisabled = false // Removed either way toBeRemoved.Data = append(toBeRemoved.Data, incRequest) @@ -2306,7 +2303,7 @@ func main() { if strings.Contains(fmt.Sprintf("%s", err), "node available") { // Disabling until UI is updated os.Setenv("SHUFFLE_SKIP_PIPELINES", "true") - tenzirDisabled = true + tenzirDisabled = true log.Printf("[ERROR] Failed to start tenzir, reason: %s", err) err = shuffle.CreateOrgNotification( @@ -2625,7 +2622,7 @@ func deployTenzirNode() error { // return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") //log.Printf("[INFO] Pipelines are enabled by user") } else { - return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") + return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") } if isKubernetes == "true" { @@ -2825,12 +2822,12 @@ func createAndStartTenzirNode(ctx context.Context, containerName, imageName stri }, } - // FIXME: Is this necessary? Seems to screw up networking: + // FIXME: Is this necessary? Seems to screw up networking: // conflicting options: hostname and the network mode /* - if isKubernetes != "true" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" { - hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) - } + if isKubernetes != "true" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" { + hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) + } */ resp, err := dockercli.ContainerCreate(ctx, config, hostConfig, networkingConfig, nil, containerName) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 996764fd..6e48043a 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -56,6 +56,7 @@ var logsDisabled = os.Getenv("SHUFFLE_LOGS_DISABLED") var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME") var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION")) +var appServiceAccountName = os.Getenv("SHUFFLE_APP_SERVICE_ACCOUNT_NAME") var baseimagename = "frikky/shuffle" var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") @@ -105,11 +106,11 @@ var window = shuffle.NewTimeWindow(10 * time.Second) // Images to be autodeployed in the latest version of Shuffle. var autoDeploy = map[string]string{ - "http:1.4.0": "frikky/shuffle:http_1.4.0", - "http:1.3.0": "frikky/shuffle:http_1.3.0", - "shuffle-tools:1.2.0": "frikky/shuffle:shuffle-tools_1.2.0", - "shuffle-subflow:1.0.0": "frikky/shuffle:shuffle-subflow_1.0.0", - "shuffle-subflow:1.1.0": "frikky/shuffle:shuffle-subflow_1.1.0", + "http:1.4.0": "frikky/shuffle:http_1.4.0", + "http:1.3.0": "frikky/shuffle:http_1.3.0", + "shuffle-tools:1.2.0": "frikky/shuffle:shuffle-tools_1.2.0", + "shuffle-subflow:1.0.0": "frikky/shuffle:shuffle-subflow_1.0.0", + "shuffle-subflow:1.1.0": "frikky/shuffle:shuffle-subflow_1.1.0", // "shuffle-tools-fork:1.0.0": "frikky/shuffle:shuffle-tools-fork_1.0.0", } @@ -592,6 +593,8 @@ func deployk8sApp(image string, identifier string, env []string) error { Env: buildEnvVars(envMap), }, }, + DNSPolicy: corev1.DNSClusterFirst, + ServiceAccountName: appServiceAccountName}, }, }, }, From e554943c8d518b87391ac4308629de5247ac4fe5 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Tue, 18 Feb 2025 16:22:48 +0100 Subject: [PATCH 002/200] add common kubernetes labels Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/onprem/orborus/orborus.go | 25 +++++++++++++++------ functions/onprem/worker/worker.go | 35 ++++++++++++++++++----------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 093faf79..894f6594 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1078,8 +1078,17 @@ func deployK8sWorker(image string, identifier string, env []string) error { } } - containerLabels := map[string]string{ - "container": "shuffle-worker", + labels := map[string]string{ + "app.kubernetes.io/name": "shuffle-worker", + "app.kubernetes.io/instance": identifier, + // "app.kubernetes.io/version": "", + "app.kuvernetes.io/part-of": "shuffle", + "app.kubernetes.io/managed-by": "shuffle-orborus", + } + + matchLabels := map[string]string{ + "app.kubernetes.io/name": "shuffle-worker", + "app.kubernetes.io/instance": identifier, } containerAttachment := corev1.Container{ @@ -1184,16 +1193,17 @@ func deployK8sWorker(image string, identifier string, env []string) error { deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: identifier, + Name: identifier, + Labels: labels, }, Spec: appsv1.DeploymentSpec{ Replicas: int32Ptr(replicaNumberInt32), Selector: &metav1.LabelSelector{ - MatchLabels: containerLabels, + MatchLabels: matchLabels, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: containerLabels, + Labels: labels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -1214,10 +1224,11 @@ func deployK8sWorker(image string, identifier string, env []string) error { // kubectl expose deployment shuffle-workers --type=NodePort --port=33333 --target-port=33333 service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: identifier, + Name: identifier, + Labels: labels, }, Spec: corev1.ServiceSpec{ - Selector: containerLabels, + Selector: matchLabels, Ports: []corev1.ServicePort{ { Protocol: "TCP", diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 996764fd..064301b9 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -487,9 +487,22 @@ func deployk8sApp(image string, identifier string, env []string) error { //fix naming convention // podUuid := uuid.NewV4().String() - // podName := fmt.Sprintf("%s-%s", value, podUuid) + // name := fmt.Sprintf("%s-%s", value, podUuid) // replace identifier "_" with "-" - podName := strings.ReplaceAll(identifier, "_", "-") + name := strings.ReplaceAll(identifier, "_", "-") + + labels := map[string]string{ + "app.kubernetes.io/name": "shuffle-app", + "app.kubernetes.io/instance": name, + // "app.kubernetes.io/version": "", + "app.kuvernetes.io/part-of": "shuffle", + "app.kubernetes.io/managed-by": "shuffle-worker", + } + + matchLabels := map[string]string{ + "app.kubernetes.io/name": "shuffle-app", + "app.kubernetes.io/instance": name, + } // pod := &corev1.Pod{ // ObjectMeta: metav1.ObjectMeta{ @@ -569,20 +582,17 @@ func deployk8sApp(image string, identifier string, env []string) error { deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: name, + Labels: labels, }, Spec: appsv1.DeploymentSpec{ Replicas: int32Ptr(replicaNumberInt32), Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": podName, - }, + MatchLabels: matchLabels, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": podName, - }, + Labels: labels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -606,12 +616,11 @@ func deployk8sApp(image string, identifier string, env []string) error { // kubectl expose deployment {podName} --type=NodePort --port=80 --target-port=80 service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: podName, + Name: name, + Labels: labels, }, Spec: corev1.ServiceSpec{ - Selector: map[string]string{ - "app": podName, - }, + Selector: matchLabels, Ports: []corev1.ServicePort{ { Protocol: "TCP", From ead70439be8464356e78cff8c8da1cf2513e59b1 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Tue, 18 Feb 2025 16:32:36 +0100 Subject: [PATCH 003/200] fix typo, format file Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/onprem/orborus/orborus.go | 30 ++++++++++++++--------------- functions/onprem/worker/worker.go | 14 +++++++------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 894f6594..bd551a99 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1082,7 +1082,7 @@ func deployK8sWorker(image string, identifier string, env []string) error { "app.kubernetes.io/name": "shuffle-worker", "app.kubernetes.io/instance": identifier, // "app.kubernetes.io/version": "", - "app.kuvernetes.io/part-of": "shuffle", + "app.kubernetes.io/part-of": "shuffle", "app.kubernetes.io/managed-by": "shuffle-orborus", } @@ -2237,7 +2237,7 @@ func main() { if incRequest.Type == "PIPELINE_CREATE" || incRequest.Type == "PIPELINE_START" || incRequest.Type == "PIPELINE_STOP" || incRequest.Type == "PIPELINE_DELETE" { os.Setenv("SHUFFLE_SKIP_PIPELINES", "false") - tenzirDisabled = false + tenzirDisabled = false // Running NEW or editing pipelines err := handlePipeline(incRequest) @@ -2262,7 +2262,7 @@ func main() { } else if incRequest.Type == "CATEGORY_UPDATE" { os.Setenv("SHUFFLE_SKIP_PIPELINES", "false") - tenzirDisabled = false + tenzirDisabled = false err = handleFileCategoryChange() if err != nil { @@ -2307,7 +2307,7 @@ func main() { // Manual command = overrides to allow starting of Tenzir from the frontend anyway. os.Setenv("SHUFFLE_SKIP_PIPELINES", "false") - tenzirDisabled = false + tenzirDisabled = false // Removed either way toBeRemoved.Data = append(toBeRemoved.Data, incRequest) @@ -2317,7 +2317,7 @@ func main() { if strings.Contains(fmt.Sprintf("%s", err), "node available") { // Disabling until UI is updated os.Setenv("SHUFFLE_SKIP_PIPELINES", "true") - tenzirDisabled = true + tenzirDisabled = true log.Printf("[ERROR] Failed to start tenzir, reason: %s", err) err = shuffle.CreateOrgNotification( @@ -2636,7 +2636,7 @@ func deployTenzirNode() error { // return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") //log.Printf("[INFO] Pipelines are enabled by user") } else { - return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") + return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") } if isKubernetes == "true" { @@ -2836,12 +2836,12 @@ func createAndStartTenzirNode(ctx context.Context, containerName, imageName stri }, } - // FIXME: Is this necessary? Seems to screw up networking: + // FIXME: Is this necessary? Seems to screw up networking: // conflicting options: hostname and the network mode /* - if isKubernetes != "true" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" { - hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) - } + if isKubernetes != "true" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" { + hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) + } */ resp, err := dockercli.ContainerCreate(ctx, config, hostConfig, networkingConfig, nil, containerName) @@ -3049,7 +3049,6 @@ func createPipeline(command, identifier string) (string, error) { return "", err } - body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Printf("[ERROR] Failed reading response body: %s", err) @@ -3067,8 +3066,8 @@ func createPipeline(command, identifier string) (string, error) { } type PipelineResponse struct { - ID string `json:"id"` - Message string `json:"message"` + ID string `json:"id"` + Message string `json:"message"` Severity string `json:"severity"` } @@ -3291,7 +3290,7 @@ func handleFileCategoryChange() error { tenzirStorageFolder = "/tmp/" } - tenzirStorageFolder = strings.TrimRight(tenzirStorageFolder, "/") + tenzirStorageFolder = strings.TrimRight(tenzirStorageFolder, "/") sigmaPath := fmt.Sprintf("%s/sigma_rules", tenzirStorageFolder) err = extractZIP("files.zip", sigmaPath) if err != nil { @@ -3299,7 +3298,6 @@ func handleFileCategoryChange() error { return err } - log.Printf("[DEBUG] Detection files copied to '%s' successfully.", sigmaPath) return nil @@ -3383,7 +3381,7 @@ func removeFileCategory() error { tenzirStorageFolder = "/tmp/" } - tenzirStorageFolder = strings.TrimRight(tenzirStorageFolder, "/") + tenzirStorageFolder = strings.TrimRight(tenzirStorageFolder, "/") //sigmaPath := "/var/lib/tenzir/sigma_rules/*" sigmaPath := fmt.Sprintf("%s/sigma_rules", tenzirStorageFolder) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 064301b9..6a580229 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -23,8 +23,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/mount" dockerclient "github.com/docker/docker/client" // This is for automatic removal of certain code :) @@ -105,11 +105,11 @@ var window = shuffle.NewTimeWindow(10 * time.Second) // Images to be autodeployed in the latest version of Shuffle. var autoDeploy = map[string]string{ - "http:1.4.0": "frikky/shuffle:http_1.4.0", - "http:1.3.0": "frikky/shuffle:http_1.3.0", - "shuffle-tools:1.2.0": "frikky/shuffle:shuffle-tools_1.2.0", - "shuffle-subflow:1.0.0": "frikky/shuffle:shuffle-subflow_1.0.0", - "shuffle-subflow:1.1.0": "frikky/shuffle:shuffle-subflow_1.1.0", + "http:1.4.0": "frikky/shuffle:http_1.4.0", + "http:1.3.0": "frikky/shuffle:http_1.3.0", + "shuffle-tools:1.2.0": "frikky/shuffle:shuffle-tools_1.2.0", + "shuffle-subflow:1.0.0": "frikky/shuffle:shuffle-subflow_1.0.0", + "shuffle-subflow:1.1.0": "frikky/shuffle:shuffle-subflow_1.1.0", // "shuffle-tools-fork:1.0.0": "frikky/shuffle:shuffle-tools-fork_1.0.0", } @@ -495,7 +495,7 @@ func deployk8sApp(image string, identifier string, env []string) error { "app.kubernetes.io/name": "shuffle-app", "app.kubernetes.io/instance": name, // "app.kubernetes.io/version": "", - "app.kuvernetes.io/part-of": "shuffle", + "app.kubernetes.io/part-of": "shuffle", "app.kubernetes.io/managed-by": "shuffle-worker", } From 065573811eb7ab80f174118ce08e305df4240c51 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:30:14 +0100 Subject: [PATCH 004/200] fix typo Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/onprem/worker/worker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 6e48043a..d212055f 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -594,7 +594,7 @@ func deployk8sApp(image string, identifier string, env []string) error { }, }, DNSPolicy: corev1.DNSClusterFirst, - ServiceAccountName: appServiceAccountName}, + ServiceAccountName: appServiceAccountName, }, }, }, From 80aa1a3fd2a1b30b60a62fab9b80088736be2d91 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Wed, 19 Feb 2025 11:30:40 +0100 Subject: [PATCH 005/200] update cleanup and label selectors Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/onprem/orborus/orborus.go | 44 ++++++----------------------- functions/onprem/worker/worker.go | 3 +- 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index bd551a99..15baf03c 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -216,8 +216,7 @@ func skipCheckInCleanup(name string) bool { func cleanupExistingNodes(ctx context.Context) error { if isKubernetes == "true" { - // of course, this doesn't clean up "nodes" but - // rather pods, services, roles etc. + // Cleanup all workers created by orborus and all apps created by workers. if kubernetesNamespace == "" { kubernetesNamespace = "default" @@ -229,62 +228,38 @@ func cleanupExistingNodes(ctx context.Context) error { return err } - // Delete all pods - pods, err := clientset.CoreV1().Pods(kubernetesNamespace).List(context.Background(), metav1.ListOptions{}) - if err != nil { - log.Printf("[ERROR] Failed listing pods: %s", err) - return err - } - - for _, pod := range pods.Items { - // check if pod.Name starts with: - // "backend-", "frontend-", "orborus-", "opensearch-" or "memcached-" - if skipCheckInCleanup(pod.Name) { - continue - } - - err := clientset.CoreV1().Pods(kubernetesNamespace).Delete(context.Background(), pod.Name, metav1.DeleteOptions{}) - if err != nil { - log.Printf("[ERROR] Failed deleting pod %s: %s", pod.Name, err) - } - } - // Delete all services - services, err := clientset.CoreV1().Services(kubernetesNamespace).List(context.Background(), metav1.ListOptions{}) + services, err := clientset.CoreV1().Services(kubernetesNamespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name in (shuffle-worker, shuffle-app),app.kubernetes.io/managed-by in (shuffle-orborus, shuffle-worker)", + }) if err != nil { log.Printf("[ERROR] Failed listing services: %s", err) return err } for _, service := range services.Items { - if skipCheckInCleanup(service.Name) { - continue - } - err := clientset.CoreV1().Services(kubernetesNamespace).Delete(context.Background(), service.Name, metav1.DeleteOptions{}) if err != nil { log.Printf("[ERROR] Failed deleting service %s: %s", service.Name, err) } } - deployments, err := clientset.AppsV1().Deployments(kubernetesNamespace).List(context.Background(), metav1.ListOptions{}) + deployments, err := clientset.AppsV1().Deployments(kubernetesNamespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name in (shuffle-worker, shuffle-app),app.kubernetes.io/managed-by in (shuffle-orborus, shuffle-worker)", + }) if err != nil { log.Printf("[ERROR] Failed listing deployments: %s", err) return err } for _, deployment := range deployments.Items { - if skipCheckInCleanup(deployment.Name) { - continue - } - err := clientset.AppsV1().Deployments(kubernetesNamespace).Delete(context.Background(), deployment.Name, metav1.DeleteOptions{}) if err != nil { log.Printf("[ERROR] Failed deleting deployment %s: %s", deployment.Name, err) } } - log.Printf("[INFO] Cleaned up all pods and services in namespace %s. Waiting 10 seconds for cleanup to reflect", kubernetesNamespace) + log.Printf("[INFO] Cleaned up all services and deployments in namespace %s. Waiting 10 seconds for cleanup to reflect", kubernetesNamespace) time.Sleep(10 * time.Second) @@ -3546,9 +3521,8 @@ func getRunningWorkers(ctx context.Context, workerTimeout int) int { return 0 } - labelSelector := "app=shuffle-worker" pods, podErr := clientset.CoreV1().Pods(kubernetesNamespace).List(ctx, metav1.ListOptions{ - LabelSelector: labelSelector, + LabelSelector: "app.kubernetes.io/name=shuffle-worker", }) if podErr != nil { log.Printf("[ERROR] Failed getting running workers: %s", podErr) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 6a580229..9e319796 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -966,7 +966,8 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] func cleanupKubernetesExecution(clientset *kubernetes.Clientset, workflowExecution shuffle.WorkflowExecution, namespace string) error { // workerName := fmt.Sprintf("worker-%s", workflowExecution.ExecutionId) - labelSelector := fmt.Sprintf("app=shuffle-app,executionId=%s", workflowExecution.ExecutionId) + // FIXME: The executionId label is currently not set + labelSelector := fmt.Sprintf("app.kubernetes.io/name=shuffle-app,executionId=%s", workflowExecution.ExecutionId) podList, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{ LabelSelector: labelSelector, From 2d00444a5a285560bc4aa613ae069a1b8c166ba7 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Thu, 20 Feb 2025 18:45:05 +0530 Subject: [PATCH 006/200] [Feature] - Add frontend testing script for onprem --- .github/workflows/quick-testing.yml | 19 +- frontend/frontend-testing.sh | 104 ++++++++++ frontend/package.json | 3 +- frontend/selenium-test.js | 249 +++++++++++++++++++++++ frontend/src/App.jsx | 1 + frontend/src/components/EditWorkflow.jsx | 2 + frontend/src/views/LoginPage.jsx | 1 + frontend/src/views/Workflows2.jsx | 1 + 8 files changed, 377 insertions(+), 3 deletions(-) create mode 100644 frontend/frontend-testing.sh create mode 100644 frontend/selenium-test.js diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 17ac28ac..db68f4ae 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -5,6 +5,7 @@ on: workflows: ["dockerbuild"] types: - completed + workflow_dispatch: jobs: build: @@ -17,9 +18,17 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 - + + - name: Set up Docker + uses: docker/setup-buildx-action@v1 + + - name: Install Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y docker-compose + - name: Set up opensearch directory - run: mkdir shuffle-database && chmod -R 777 shuffle-database + run: mkdir -p shuffle-database && chmod -R 777 shuffle-database - name: Build the stack run: docker-compose up -d @@ -95,6 +104,12 @@ jobs: echo "User registration failed after $MAX_RETRIES attempts." exit 1 + - name: Run Selenium testing for frontend + run: | + cd $GITHUB_WORKSPACE/frontend + chmod +x frontend-testing.sh + ./frontend-testing.sh + - name: Get the API key and run a health check run: | RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search') diff --git a/frontend/frontend-testing.sh b/frontend/frontend-testing.sh new file mode 100644 index 00000000..454f9a12 --- /dev/null +++ b/frontend/frontend-testing.sh @@ -0,0 +1,104 @@ + +SERVER_URL="http://localhost:3000" + +declare -a Routes=() + +# Add all cloud routes here +if [[ "$SERVER_URL" == "http://localhost:3001" || "$SERVER_URL" == "http://localhost:3002" || "$SERVER_URL" == "https://sandbox.shuffler.io" || "$SERVER_URL" == "https://shuffler.io" ]]; then +Routes+=( +'homepage' +'home' +'contact' +'workflows/fa9314a7-bb9b-4b41-8885-1d02b199f04d' +'services' +'debug' +'creator' +'articles' +'partners' +'pricing' +'pricing2' +'training' +'professional-services' +'detections' +) +fi + +# Add all common routes here +Routes+=( +'workflows' +'workflows?tab=org_workflows' +'workflows?tab=my_workflows' +'workflows?tab=all_workflows' +'usecases' +'usecases2' +'getting-started' +'welcome' +'health' +'docs' +'settings' +'apps/new' +'apps/gmail' +'apis/gmail' +'forms' +'apps' +'apps?tab=my_apps' +'apps?tab=all_apps' +'search' +'search?tab=org_apps' +'search?tab=my_apps' +'search?tab=workflows' +'search?tab=docs' +'search?tab=creators' +'search?tab=discord' +"admin?tab=organization" +"admin?tab=users" +"admin?tab=app_auth" +"admin?tab=datastore" +"admin?tab=files" +"admin?tab=triggers" +"admin?tab=locations" +"admin?tab=tenants" +"admin?admin_tab=org_config" +"admin?admin_tab=sso" +"admin?admin_tab=notifications" +"admin?admin_tab=billingstats" +"admin?admin_tab=branding(beta)" +) + +ALL_ROUTES=("${Routes[@]}") + +# Stop frontend container to so it test unpushed changes to shuffle-frontend container +docker stop shuffle-frontend + +# Install dependencies +yarn install +echo "Starting frontend..." + +BROWSER=none yarn start & +SERVER_PID=$! +echo "Frontend started with PID: $SERVER_PID" + +echo "Waiting for 1 minute to ensure the server is fully up..." +sleep 60 + +echo "Server is up! Starting Selenium tests..." + +echo "Starting frontend tests..." +node selenium-test.js "$SERVER_URL" "${ALL_ROUTES[@]}" + +TEST_EXIT_CODE=$? + +if [[ $TEST_EXIT_CODE -ne 0 ]]; then +echo "Selenium tests failed. Exiting..." +kill $SERVER_PID +exit 1 +fi + +kill $SERVER_PID +echo "Testing complete. See above logs for errors if any." + +# Starting frontend container +echo "Starting shuffle-frontend container..." +docker start shuffle-frontend +echo "shuffle-frontend started successfully." +exit 0 \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 7426ee23..4f6d2166 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -88,6 +88,7 @@ "remark-rehype": "^11.0.0", "rsuite": "^5.23.0", "search-insights": "^2.2.1", + "selenium-webdriver": "^4.28.1", "shellwords": "^1.0.1", "simplebar": "^4.2.3", "styled-components": "^4.4.1", @@ -98,7 +99,7 @@ "zone.js": "~0.8.26" }, "scripts": { - "start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start", + "start": "set HTTPS=false&& set PORT=3000&& set GENERATE_SOURCEMAP=true&& react-scripts --openssl-legacy-provider start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", diff --git a/frontend/selenium-test.js b/frontend/selenium-test.js new file mode 100644 index 00000000..eb9b731e --- /dev/null +++ b/frontend/selenium-test.js @@ -0,0 +1,249 @@ +const { Builder, By, until } = require('selenium-webdriver'); +const chrome = require('selenium-webdriver/chrome'); +const fs = require('fs'); +const path = require('path'); + +console.log('Starting Selenium script for testing pages...'); + +(async () => { + const userDataDir = path.join(__dirname, 'chrome-user-data', `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`); + fs.mkdirSync(userDataDir, { recursive: true }); + + let options = new chrome.Options(); + options.addArguments('--no-sandbox'); + options.addArguments('--headless'); + + let driver = await new Builder() + .forBrowser('chrome') + .setChromeOptions(options) + .build(); + + const SuccessfullyLoadedPath = []; + const FailedToLoadPath = []; + + try { + await driver.manage().window().setRect({ width: 1600, height: 1200 }); + await driver.manage().setTimeouts({ implicit: 15000 }); + + const frontendURL = process.argv[2]; + const routes = process.argv.slice(3); + + console.log('Frontend URL:', frontendURL); + const isCloud = frontendURL === 'http://localhost:3002' || frontendURL === 'https://sandbox.shuffler.io' || frontendURL === 'https://shuffler.io'; + + if (isCloud) { + // Login Credentials + const LOGIN_URL = `${frontendURL}/login`; + const USERNAME = 'shuffle-testing@gmail.com'; + const PASSWORD = 'testing@123'; + + console.log('Logging in...'); + await driver.get(LOGIN_URL); + + try { + await driver.wait(until.elementLocated(By.css('#emailfield')), 10000); + await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME); + + await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000); + await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD); + + await driver.wait(until.elementLocated(By.css('#loginButton')), 10000); + await driver.findElement(By.css('#loginButton')).click(); + + // Ensure login success by checking URL change + await driver.wait(async () => { + const url = await driver.getCurrentUrl(); + return url.includes('welcome'); + }, 20000); + + console.log('Successfully logged in!'); + } catch (error) { + console.error('Login failed:', error.message); + FailedToLoadPath.push(LOGIN_URL); + await driver.quit(); + process.exit(1); + } + }else { + // Steps for onprem testing + // 1. Login + // 2. Create new workflow + + // Write your own login credentials here if you are testing onprem locally + const USERNAME = 'demo@demo.io'; + const PASSWORD = 'supercoolpassword'; + const WORKFLOW_URL = `${frontendURL}/workflows`; + + try { + // Login + console.log('Logging in...'); + await driver.get(`${frontendURL}/login`); + + await driver.wait(until.elementLocated(By.css('#emailfield')), 10000); + await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME); + + await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000); + await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD); + + await driver.wait(until.elementLocated(By.css('#loginButton')), 10000); + await driver.findElement(By.css('#loginButton')).click(); + + + // Ensure signup success by checking URL change + await driver.wait(async () => { + const url = await driver.getCurrentUrl(); + return url.includes('welcome') || url.includes('workflows'); + }, 20000); + + + const isParentPresent = await driver.wait(async () => { + return await driver.executeScript( + "return document.querySelector('.parent-component') !== null;" + ); + }, 5000).catch(() => false); + + if (!isParentPresent) { + const logs = await driver.manage().logs().get('browser'); + const severeErrors = logs.filter(log => log.level.name === 'SEVERE'); + if (severeErrors.length > 0) { + const crashCausingErrors = severeErrors.filter(err => { + return !err.message.includes('Warning:') && + !err.message.includes('MUI:') + }); + + console.error(`Page (${frontendURL}/login) did not load correctly:`, crashCausingErrors); + FailedToLoadPath.push(`${frontendURL}/login`); + } + } + + console.log('Successfully logged in!'); + + } catch (error) { + console.error('Failed to login:', error.message); + FailedToLoadPath.push(WORKFLOW_URL); + await driver.quit(); + process.exit(1); + } + + try { + // Create new workflow + console.log('Creating new workflow...'); + await driver.get(WORKFLOW_URL); + + await driver.sleep(1000); + + await driver.wait(until.elementLocated(By.css('#create_workflow_button')), 10000); + const create_workflow_button = await driver.findElement(By.css('#create_workflow_button')); + + await driver.sleep(1000); + await create_workflow_button.click(); + + await driver.sleep(1500); + + await driver.wait(until.elementLocated(By.css('#Enter-Workflow-Name')), 10000); + const enter_workflow_name_field = await driver.findElement(By.css('#Enter-Workflow-Name')); + + await driver.sleep(1000); + await enter_workflow_name_field.sendKeys("Test Workflow"); + + // Wait before saving + await driver.sleep(1500); + + await driver.wait(until.elementLocated(By.css('#save_workflow_button')), 10000); + const save_workflow_button = await driver.findElement(By.css('#save_workflow_button')); + + await driver.sleep(1000); // Delay before clicking + await save_workflow_button.click(); + + // Wait for the parent component to appear + await driver.sleep(2000); + + const isParentPresent = await driver.wait(async () => { + return await driver.executeScript( + "return document.querySelector('.parent-component') !== null;" + ); + }, 5000).catch(() => false); + + if (!isParentPresent) { + const logs = await driver.manage().logs().get('browser'); + const severeErrors = logs.filter(log => log.level.name === 'SEVERE'); + if (severeErrors.length > 0) { + const crashCausingErrors = severeErrors.filter(err => { + return !err.message.includes('Warning:') && + !err.message.includes('MUI:'); + }); + + console.error(`Page (${WORKFLOW_URL}) did not load correctly:`, crashCausingErrors); + FailedToLoadPath.push(`${WORKFLOW_URL}`); + } + } + + } catch (error) { + console.error('Failed to create new workflow:', error.message); + FailedToLoadPath.push(WORKFLOW_URL); + await driver.quit(); + process.exit(1); + } + } + + // Get routes from command line arguments + if (routes.length === 0) { + console.error('No routes found to test.'); + await driver.quit(); + process.exit(1); + } + console.log(`Found ${routes.length} routes to test.`); + + for (const route of routes) { + const url = `${frontendURL}/${route}`; + console.log(`Testing route: ${url}`); + + try { + await driver.get(url); + await driver.sleep(3000); + + // Wait for document readiness + await driver.wait(async () => { + return await driver.executeScript('return document.readyState') === 'complete'; + }, 10000); + + const isParentPresent = await driver.wait(async () => { + return await driver.executeScript( + "return document.querySelector('.parent-component') !== null;" + ); + }, 5000).catch(() => false); + + if (!isParentPresent) { + const logs = await driver.manage().logs().get('browser'); + const severeErrors = logs.filter(log => log.level.name === 'SEVERE'); + if (severeErrors.length > 0) { + const crashCausingErrors = severeErrors.filter(err => { + return !err.message.includes('Warning:') && + !err.message.includes('MUI:') + }); + + console.error(`Page (${url}) did not load correctly:`, crashCausingErrors); + FailedToLoadPath.push(url); + } + continue; + } + + console.log(`Successfully loaded: ${url}`); + SuccessfullyLoadedPath.push(url); + } catch (error) { + console.error(`Failed to load ${url}:`, error.message); + } + } + } finally { + console.log("Total pages tested: ", SuccessfullyLoadedPath.length + FailedToLoadPath.length); + console.log("Successfully loaded pages: ", SuccessfullyLoadedPath.length); + if (FailedToLoadPath.length > 0) { + console.log("Failed to load pages: ", FailedToLoadPath.length); + console.log("Failed to load pages paths: ", FailedToLoadPath); + process.exit(1); + }else { + console.log("No pages failed to load. Congrats!"); + } + await driver.quit(); + fs.rmSync(userDataDir, { recursive: true, force: true }); + } +})(); \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 8c0e8870..03d4c77e 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -186,6 +186,7 @@ const App = (message, props) => { color: "rgba(255, 255, 255, 0.65)", minHeight: "100vh", }} + className='parent-component' > { + ) : ( + - - - - - - -
-
Insights
-
- -
-
Sessions Overview
-
-
-
- 2.8 Hours -
-
- Avg. Activity per session -
-
-
-
- /usercases/edr to ticket -
-
- Last visited page -
-
-
-
- /workflow/email management -
-
- Most visited page -
-
-
-
- - ); + // // Options for the chart + // const options = { + // scales: { + // yAxes: [ + // { + // ticks: { + // beginAtZero: true + // } + // } + // ] + // }, + // }; + // return ( + //
+ //
+ //
Timeline
+ //
+ //
+ //
+ //
+ //
+ //
Apps
+ // Category + //
+ // { setExpand(prevExpand => !prevExpand); }} style={{ color: "#FF8444" }}>Expand + //
+ // {expand ? null : + //
+ //
+ // Onboarding + // + // + //
+ // + // {checked ? + //
: + // null + // } + //
+ //
+ // + //
+ // + // {checked ? + //
: + // null + // } + //
+ //
+ // + //
+ // + // {checked ? + //
: + // null + // } + //
+ //
+ //
+ //
+ //
+ //
+ // Other + // + // + //
+ // + // {checked ? + //
: + // null + // } + //
+ //
+ // + //
+ // + // {checked ? + //
: + // null + // } + //
+ //
+ // + //
+ // + // {checked ? + //
: + // null + // } + //
+ //
+ //
+ //
+ //
} + //
+ //
+ //
Workflows
+ //
+ // + // + // + //
+ // + //
+ //
+ //
+ //
Insights
+ //
+ //
+ //
+ //
Sessions Overview
+ //
+ //
+ //
+ // 2.8 Hours + //
+ //
+ // Avg. Activity per session + //
+ //
+ //
+ //
+ // /usercases/edr to ticket + //
+ //
+ // Last visited page + //
+ //
+ //
+ //
+ // /workflow/email management + //
+ //
+ // Most visited page + //
+ //
+ //
+ //
+ //
+ // ); }; export default AnalyticsTab; diff --git a/frontend/src/components/DashboardBarchart.jsx b/frontend/src/components/DashboardBarchart.jsx index 58f20708..74970549 100644 --- a/frontend/src/components/DashboardBarchart.jsx +++ b/frontend/src/components/DashboardBarchart.jsx @@ -65,80 +65,83 @@ export const LoadStats = (globalUrl, cachekey) => { } const DashboardBarchart = (props) => { - const { timelineData, title, height, } = props; - var inputHeight = 15 - if (height !== undefined && height !== null) { - inputHeight = height - } + // this is clearly unfinished and not worth my time. + // refer to health page to see how i made it work there w/o using chartjs - const barOptions = { - plugins: { - tooltip: { - enabled: true, // Ensure tooltips are enabled - }, - }, - tooltips: { - mode: 'index', - intersect: false, - }, - legend: { - display: false - }, - layout: { - padding: { - top: 0, // Adjust the top padding as needed - bottom: -10, // Adjust the bottom padding as needed - left: 0, // Adjust the left padding as needed - right: 0, // Adjust the right padding as needed - }, - }, - scales: { - y: { - beginAtZero: false, - }, - yAxes: [{ - ticks: { - display: false - }, - beginAtZero: false, - }], - xAxes: [{ - ticks: { - display: false - }, - beginAtZero: false, - }] - }, - tooltips: { - callbacks: { - label: function (tooltipItem, data) { - const label = data.labels[tooltipItem.index] - return label.split('\n')[0] - }, - afterLabel: function (tooltipItem, data) { - const amount = tooltipItem.value === undefined || tooltipItem.value === null ? 0 : tooltipItem.value - return `Amount: ${amount}` - }, - title: function () { - return title === undefined ? '' : title - } - } - } - } + // const { timelineData, title, height, } = props; + // var inputHeight = 15 + // if (height !== undefined && height !== null) { + // inputHeight = height + // } - return ( - { - if (elements && elements.length > 0) { - //toast("Click event") - console.log("Clicked: ", elements) - } - }} - /> - ) + // const barOptions = { + // plugins: { + // tooltip: { + // enabled: true, // Ensure tooltips are enabled + // }, + // }, + // tooltips: { + // mode: 'index', + // intersect: false, + // }, + // legend: { + // display: false + // }, + // layout: { + // padding: { + // top: 0, // Adjust the top padding as needed + // bottom: -10, // Adjust the bottom padding as needed + // left: 0, // Adjust the left padding as needed + // right: 0, // Adjust the right padding as needed + // }, + // }, + // scales: { + // y: { + // beginAtZero: false, + // }, + // yAxes: [{ + // ticks: { + // display: false + // }, + // beginAtZero: false, + // }], + // xAxes: [{ + // ticks: { + // display: false + // }, + // beginAtZero: false, + // }] + // }, + // tooltips: { + // callbacks: { + // label: function (tooltipItem, data) { + // const label = data.labels[tooltipItem.index] + // return label.split('\n')[0] + // }, + // afterLabel: function (tooltipItem, data) { + // const amount = tooltipItem.value === undefined || tooltipItem.value === null ? 0 : tooltipItem.value + // return `Amount: ${amount}` + // }, + // title: function () { + // return title === undefined ? '' : title + // } + // } + // } + // } + + // return ( + // { + // if (elements && elements.length > 0) { + // //toast("Click event") + // console.log("Clicked: ", elements) + // } + // }} + // /> + // ) } export default DashboardBarchart; diff --git a/frontend/src/components/OrganizationTab.jsx b/frontend/src/components/OrganizationTab.jsx index 3fc2cbee..1911bfad 100644 --- a/frontend/src/components/OrganizationTab.jsx +++ b/frontend/src/components/OrganizationTab.jsx @@ -3,7 +3,6 @@ import { Link, useNavigate, useLocation } from "react-router-dom"; import Billing from "../components/Billing.jsx"; import Priorities from "../components/Priorities.jsx"; import Branding from "../components/Branding.jsx"; -import AnalyticsTab from '../components/AnalyticsTab.jsx'; import EditOrgTab from '../components/EditOrgTab.jsx'; import CloudSyncTab from '../components/CloudSyncTab.jsx'; import SSOTab from "../components/ssoTab.jsx" diff --git a/frontend/src/views/DashboardViews.jsx b/frontend/src/views/DashboardViews.jsx index 9f913261..4bf532a6 100644 --- a/frontend/src/views/DashboardViews.jsx +++ b/frontend/src/views/DashboardViews.jsx @@ -6,12 +6,9 @@ import classNames from "classnames"; import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; -// react plugin used to create charts -//import { Line, Bar } from "react-chartjs-2"; -//import { useAlert import { ToastContainer, toast } from "react-toastify" import Draggable from "react-draggable"; -import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; +// import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; import { Autocomplete, @@ -311,9 +308,13 @@ const RadialChart = ({keys, setSelectedCategory}) => { //axis={} } + +// 0x0elliot: not cleaning this up. this seems clearly experimental. not worth my time. + // This is the start of a dashboard that can be used. // What data do we fill in here? Idk const Dashboard = (props) => { + const { globalUrl, isLoggedIn } = props; //const alert = useAlert(); const [bigChartData, setBgChartData] = useState("data1"); From aa2ded6973d755b328d9a7cd93e13a0a4ec2dae2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 28 Feb 2025 14:00:59 +0100 Subject: [PATCH 028/200] Fixed the cache view slightly --- frontend/src/components/CacheView.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 5ff867d6..34b4608f 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -126,7 +126,7 @@ const CacheView = memo((props) => { const listOrgCache = (orgId, category) => { - const url = `${globalUrl}/api/v1/orgs/${orgId}/list_cache${category !== undefined ? `?category=${category.replaceAll(" ", "_")}` : ""}` + const url = `${globalUrl}/api/v1/orgs/${orgId}/list_cache${category !== undefined ? `?category=${category.replaceAll(" ", "%20")}` : ""}` fetch(url, { method: "GET", headers: { From 3b16d94ebc60a8e601653908a9f9d16cc45c4c54 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 28 Feb 2025 14:07:18 +0100 Subject: [PATCH 029/200] Autoscale nightly --- docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 84b1f321..b3c4dbdb 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: frontend: - image: ghcr.io/shuffle/shuffle-frontend:latest + image: ghcr.io/shuffle/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -14,7 +14,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: @@ -33,7 +33,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: @@ -53,9 +53,9 @@ services: - SHUFFLE_PASS_WORKER_PROXY=${SHUFFLE_PASS_WORKER_PROXY} - SHUFFLE_PASS_APP_PROXY=${SHUFFLE_PASS_APP_PROXY} - SHUFFLE_STATS_DISABLED=true - - SHUFFLE_WORKER_SCALE=run - SHUFFLE_LOGS_DISABLED=true - - SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:latest + - SHUFFLE_SWARM_CONFIG=run + - SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:nightly env_file: .env restart: unless-stopped security_opt: From 410a61af1c39984ca613af263d41fdfbde1b88d8 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 10:52:41 +0100 Subject: [PATCH 030/200] Workflow syncs --- frontend/src/components/AppModal.jsx | 82 ++- frontend/src/components/ParsedAction.jsx | 6 + .../src/components/ShuffleCodeEditor1.jsx | 1 + frontend/src/views/AngularWorkflow.jsx | 581 +++++++++++++++--- frontend/src/views/Apps2.jsx | 3 +- 5 files changed, 569 insertions(+), 104 deletions(-) diff --git a/frontend/src/components/AppModal.jsx b/frontend/src/components/AppModal.jsx index c3deb1ae..e9364913 100644 --- a/frontend/src/components/AppModal.jsx +++ b/frontend/src/components/AppModal.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { memo, useCallback, useEffect, useState } from 'react'; import { useNavigate } from 'react-router'; import { @@ -29,8 +29,15 @@ import theme from "../theme.jsx"; import YAML from 'yaml'; import { toast } from 'react-toastify'; import { Link } from 'react-router-dom'; +import { InstantSearch, connectHits, connectSearchBox } from 'react-instantsearch-dom'; +import algoliasearch from "algoliasearch/lite"; -const AppModal = ({ open, onClose, app, globalUrl, getApps }) => { +const searchClient = algoliasearch( + "JNSS5CFDZZ", + "db08e40265e2941b9a7d8f644b6e5240" +);; + +const AppModal = ({ open, onClose, app, globalUrl, getApps}) => { const [frameworkData, setFrameworkData] = useState({}) const [userdata, setUserdata] = useState({}) @@ -79,8 +86,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => { }); }, [app]); - - const getFramework = () => { + const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", headers: { @@ -295,8 +301,6 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => { }) } - - useEffect(() => { setUsecaseLoading(true) getAvailableWorkflows() @@ -329,6 +333,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => { //delete apps from local storage localStorage.removeItem("apps"); getApps(); + onClose(); }, 1000); } else { toast("Failed deleting app. Does it still exist?"); @@ -720,18 +725,7 @@ const AppModal = ({ open, onClose, app, globalUrl, getApps }) => { textAlign: "start", flex: 1, }}> - - 20 - + { }; export default AppModal; + + +const WorkflowCard = memo(({ app }) => { + const [name, setName] = useState(""); + const [relatedWorkflows, setRelatedWorkflows] = useState(0); + + const WorkflowHits = ({ hits }) => { + useEffect(() => { + setRelatedWorkflows(hits?.length || 0); + }, [hits]); + + return null; + }; + + const CustomWorkflowHits = connectHits(WorkflowHits); + + const SearchBox = ({ refine, currentRefinement }) => { + useEffect(() => { + if (name.length > 0 && currentRefinement !== name) { + refine(name?.split(" ")[0]); + } + }, [name, refine, currentRefinement]); + + return null; + }; + + const CustomSearchBox = connectSearchBox(SearchBox); + + useEffect(() => { + if (app?.name && app.name.trim().length > 0 && name !== app.name) { + const formattedName = app.name + .charAt(0) + .toUpperCase() + app.name.substring(1) + .replaceAll("_", " "); + setName(formattedName); + } + }, [app?.name]); + + return ( + + {name.length > 0 ? ( + + + + {relatedWorkflows} + + ) : ( + relatedWorkflows + )} + + ); +}); diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 4732efc2..75cfc743 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1160,6 +1160,12 @@ const ParsedAction = (props) => { selectedActionParameters[1].value = splitparsed[1] selectedAction.parameters[1].value = splitparsed[1] + + if (splitparsed.length > 2) { + toast.warn("Filter list only supports filtering at the first level. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.") + } else if (selectedAction.parameters[1].value.includes(".#")) { + toast.warn("This filter may not work due to using .# indexing. Please use the 'execute python' action and try the 'filter a list' function in the code editor.") + } } else { // Remove .# and after const splitparsed = parsedvalue.split(".#") diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index 1f713c5a..2601af05 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -89,6 +89,7 @@ const liquidFilters = [ const pythonFilters = [ { "name": "Hello World", "value": `print("hello world")`, "example": `` }, { "name": "Using Shuffle variables", "value": `import json\nnodevalue = r\"\"\"$exec\"\"\"\nif not nodevalue:\n nodevalue = r\"\"\"{\"sample\": \"string\", \"int\": 1}\"\"\"\n \njsondata = json.loads(nodevalue)\nprint(jsondata)`, "example": `` }, + { "name": "Filter a list", "value": `import json\nnodevalue = r\"\"\"$exec\"\"\"\nif not nodevalue:\n nodevalue = r\"\"\"[{\"sample\": \"string\", \"int\": 1, "malicious": "no"}, {\"sample\": \"string2\", \"int\": 1, "malicious": "yes"}]\"\"\"\n \njsondata = json.loads(nodevalue)\nfiltered = []\nfor item in jsondata:\n try:\n if item[\"malicious\"] == \"yes\":\n filtered.append(item)\n except:\n pass\nprint(json.dumps(filtered))`, "example": `` }, { "name": "Print Execution ID", "value": `print(self.current_execution_id)`, "example": `` }, { "name": "Get full execution details", "value": `print(self.full_execution)`, "example": `` }, { "name": "Use files", "value": `# Create a sample file\nfiles = [{\n \"filename\": \"test.txt\",\n \"data\": \"Testdata\"\n}]\nret = self.set_files(files)\n\n# Get the content of the file from Shuffle storage\n# Originally a byte string in the \"data\" key\nfile_content = (self.get_file(ret[0])[\"data\"]).decode()\nprint(file_content)`, "example": `` }, diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index bdbaf1c2..16d5e9d0 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -74,6 +74,7 @@ import { import { + Storage as StorageIcon, Code as CodeIcon, Folder as FolderIcon, VerifiedUser as VerifiedUserIcon, @@ -503,7 +504,7 @@ const AngularWorkflow = (defaultprops) => { const [allRevisions, setAllRevisions] = useState([]) const [menuPosition, setMenuPosition] = useState(null); const [showDropdown, setShowDropdown] = React.useState(false); - const [subflowActionList, setSubflowActionList] = React.useState([]); + const [triggerActionList, setTriggerActionList] = React.useState([]); const [apps, setApps] = React.useState([]); const [filteredApps, setFilteredApps] = React.useState([]); @@ -574,11 +575,12 @@ const AngularWorkflow = (defaultprops) => { }, [editWorkflowModalOpen]) useEffect(() => { - if (selectedTrigger !== undefined && selectedTrigger?.parameters !== undefined && selectedTrigger?.parameters !== null && selectedTrigger?.parameters?.length > 1) { - // Right now just setting for the subflow - setSelectedTriggerValue(selectedTrigger?.parameters[1]?.value) - } - }, [selectedTrigger]) + if(selectedTrigger?.trigger_type === "SUBFLOW"){ + setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[1].value || "") + } else if(selectedTrigger?.trigger_type === "USERINPUT"){ + setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[0].value || "Do you want to continue the workflow? Start parameters: $exec") + } + }, [selectedTriggerIndex, selectedTrigger, workflow]) useEffect(() => { if(selectedEdge && Object.keys(selectedEdge).length > 0){ @@ -1136,23 +1138,66 @@ const AngularWorkflow = (defaultprops) => { useEffect(() => { // if (subflowActionList.length === 0) { const newActionList = []; - // FIXME: Have previous execution values in here - newActionList.push({ - type: "Runtime Argument", - name: "Runtime Argument", - value: "$exec", - highlight: "exec", - autocomplete: "exec", - example: "hello", - }) - newActionList.push({ - type: "Shuffle Database", - name: "Shuffle Database", + + + if (workflowExecutions.length > 0) { + for (let execution of workflowExecutions) { + const execArg = execution.execution_argument; + if (execArg && execArg.length > 0) { + const valid = validateJson(execArg); + if (valid.valid) { + newActionList.push({ + type: "Runtime Argument", + name: "Runtime Argument", + value: "$exec", + highlight: "exec", + autocomplete: "exec", + example: valid.result, + }) + + break + } + } + } + } + + // Add default Runtime Argument if none were added + if (newActionList.length === 0) { + newActionList.push({ + type: "Runtime Argument", + name: "Runtime Argument", + value: "$exec", + highlight: "exec", + autocomplete: "exec", + example: "", + }) + } + // Add Shuffle DB with cache keys if available + let cacheKey = { + type: "Shuffle DB", + name: "Shuffle DB", value: "$shuffle_cache", - highlight: "shuffle_db", + highlight: "shuffle_cache", autocomplete: "shuffle_cache", - example: "hello", - }) + example: "Hello", + } + + if (listCache?.keys?.length > 0) { + cacheKey.example = {}; + for (let item of listCache.keys) { + if (item.key) { + let itemValue = item.value ?? ""; + if (itemValue.length > 10000) { + itemValue = ""; + } + cacheKey.example[item.key.split(" ").join("_")] = { value: itemValue }; + } + } + } + + newActionList.push(cacheKey); + // FIXME: Have previous execution values in here + if ( workflow.workflow_variables !== null && workflow.workflow_variables !== undefined && @@ -1216,7 +1261,7 @@ const AngularWorkflow = (defaultprops) => { const validated = validateJson(foundResult.result) if (validated.valid) { - exampledata = validateJson.result + exampledata = validated.result break } } @@ -1234,7 +1279,7 @@ const AngularWorkflow = (defaultprops) => { } } - setSubflowActionList(newActionList); + setTriggerActionList(newActionList); }, [workflow.workflow_variables, workflow.execution_variables, workflow, selectedTrigger]); @@ -13779,6 +13824,7 @@ const AngularWorkflow = (defaultprops) => { setSourceValue({}); setConditionValue({}); setDestinationValue({}); + navigate("") }} color="secondary" > @@ -13789,6 +13835,7 @@ const AngularWorkflow = (defaultprops) => { variant="contained" onClick={() => { setSelectedEdge({}); + navigate("") var data = { condition: conditionValue, @@ -14613,43 +14660,62 @@ const AngularWorkflow = (defaultprops) => { }; const handleItemClick = (values) => { - console.log("VALUES: ", values) - if (values === undefined || values === null || values.length === 0) { - return; + console.log("VALUES: ", values) + if (values === undefined || values === null || values.length === 0) { + return; + } + + // Get the base autocomplete value and normalize it + let toComplete = values[0].autocomplete.toLowerCase().replaceAll(" ", "_"); + + // Add $ prefix if not already present + if (!toComplete.startsWith("$")) { + toComplete = "$" + toComplete; + } + + // Add any additional path components + for (let key in values) { + if (key === "0" || !values[key].autocomplete) { + continue; } + toComplete += values[key].autocomplete; + } - /* - workflow.triggers[selectedTriggerIndex].parameters[1].value - .trim() - .endsWith("$") - ? values[0].autocomplete - : "$" + values[0].autocomplete; + var foundField; + // Update the field value + if(selectedTrigger?.trigger_type === "SUBFLOW"){ + foundField = document.getElementById("subflow_exec_field"); + }else if(selectedTrigger?.trigger_type === "USERINPUT"){ + foundField = document.getElementById("userinput_info_field"); + } - for (var key in values) { - if (key === 0 || values[key].autocomplete.length === 0) { - continue; - } + if (foundField) { + // Get current cursor position + const cursorPos = foundField.selectionStart; + const currentValue = foundField.value; - toComplete += values[key].autocomplete - } - */ - - if (selectedTrigger.name === "Shuffle Workflow") { - const toComplete = workflow?.triggers?.[selectedTriggerIndex]?.parameters?.[1]?.value + "$" + values[0]?.autocomplete - // selectedTrigger.parameters[1].value = toComplete - workflow.triggers[selectedTriggerIndex].parameters[1].value = toComplete - const foundfield = document.getElementById("subflow_exec_field") - if (foundfield !== undefined && foundfield !== null) { - foundfield.value = toComplete - } - setWorkflow(workflow) + // Insert the new value at cursor position + const newValue = currentValue.slice(0, cursorPos) + toComplete + currentValue.slice(cursorPos); + foundField.value = newValue; + + // Update state + setSelectedTriggerValue(newValue); + if(selectedTrigger?.trigger_type === "SUBFLOW"){ + workflow.triggers[selectedTriggerIndex].parameters[1].value = newValue + }else if(selectedTrigger?.trigger_type === "USERINPUT"){ + workflow.triggers[selectedTriggerIndex].parameters[0].value = newValue } - setUpdate(Math.random()); - setShowDropdown(false); - setMenuPosition(null); - }; + // Set cursor position after inserted text + foundField.setSelectionRange(cursorPos + toComplete.length, cursorPos + toComplete.length); + } + + setWorkflow(workflow); + setUpdate(Math.random()); + setShowDropdown(false); + setMenuPosition(null); +}; const subflowtypes = [ { @@ -14676,7 +14742,7 @@ const AngularWorkflow = (defaultprops) => { workflow.triggers[triggerIndex].parameters[paramIndex].value = newData; // Update workflow state to trigger re-render - setWorkflow({...workflow}); + setWorkflow(workflow); setSelectedTriggerValue(newData) setLastSaved(false); } @@ -15166,7 +15232,7 @@ const AngularWorkflow = (defaultprops) => { "name": workflow.triggers[selectedTriggerIndex].parameters[1].name, "value": parsedvalue, "field_number": 1, - "actionlist": subflowActionList, + "actionlist": triggerActionList, "field_id": "subflow_exec_field", }) }} @@ -15211,15 +15277,13 @@ const AngularWorkflow = (defaultprops) => { fullWidth color="primary" placeholder="Some execution data" - value={ - selectedTriggerValue - } + value={selectedTriggerValue || ""} onChange={(e) => { setLastSaved(false) setSelectedTriggerValue(e.target.value) }} - onBlur={() => { - workflow.triggers[selectedTriggerIndex].parameters[1].value = selectedTriggerValue + onBlur={(e) => { + workflow.triggers[selectedTriggerIndex].parameters[1].value = e.target.value setWorkflow(workflow) }} /> @@ -15237,16 +15301,17 @@ const AngularWorkflow = (defaultprops) => { marginTop: 2, }} > - {subflowActionList.map((innerdata) => { + {triggerActionList.map((innerdata) => { const icon = innerdata.type === "action" ? ( ) : innerdata.type === "workflow_variable" || innerdata.type === "execution_variable" ? ( - ) : ( - - ); + ) : innerdata.type === "Shuffle DB" ? + + : + const handleExecArgumentHover = (inside) => { var exec_text_field = document.getElementById( @@ -16682,21 +16747,43 @@ const AngularWorkflow = (defaultprops) => {
*/}
-
-
- Information - - The information you want to show the user. Supports variables. Supports Markdown & HTML. - -
+
+
+ Information + + { + event.preventDefault() + setCodeEditorModalOpen(true) + setActiveDialog("codeeditor") + var parsedvalue = workflow?.triggers[selectedTriggerIndex]?.parameters[0]?.value + + navigate(`?trigger_id=${selectedTrigger.id}&trigger_field=${"alertinfo"}&trigger_name=${selectedTrigger.label}`) + setEditorData({ + "name": workflow.triggers[selectedTriggerIndex].parameters[0].name, + "value": parsedvalue, + "field_number": 0, + "actionlist": triggerActionList, + "field_id": "information_field", + }) + }} + > + + + +
+ + The information you want to show the user. Supports variables. Supports Markdown & HTML. +
{ InputProps={{ style: { }, + endAdornment: ( + + + { + setMenuPosition({ + top: event.pageY + 10, + left: event.pageX + 10, + }); + //setShowDropdownNumber(3) + setShowDropdown(true); + }} + /> + + + ), }} fullWidth rows="4" multiline - defaultValue={ - workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters.length > 0 && workflow.triggers[selectedTriggerIndex].parameters[0] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[0].value !== undefined ? workflow.triggers[selectedTriggerIndex].parameters[0].value : "" - } + value={selectedTriggerValue} + onChange={(e) => { + setLastSaved(false) + setSelectedTriggerValue(e.target.value) + }} color="primary" placeholder="" onBlur={(e) => { setTriggerTextInformationWrapper(e.target.value); }} /> + {!showDropdown ? null : + { + handleMenuClose(); + }} + open={!!menuPosition} + style={{ + border: `2px solid #FF8544`, + color: "white", + marginTop: 2, + }} + > + {triggerActionList.map((innerdata) => { + const icon = + innerdata.type === "action" ? ( + + ) : innerdata.type === "workflow_variable" || + innerdata.type === "execution_variable" ? ( + + ) : innerdata.type === "Shuffle DB" ? + + : + + + const handleExecArgumentHover = (inside) => { + var exec_text_field = document.getElementById( + "execution_argument_input_field" + ); + if (exec_text_field !== null) { + if (inside) { + exec_text_field.style.border = "2px solid #FF8544"; + } else { + exec_text_field.style.border = ""; + } + } + + // Also doing arguments + if ( + workflow.triggers !== undefined && + workflow.triggers !== null && + workflow.triggers.length > 0 + ) { + for (let triggerkey in workflow.triggers) { + const item = workflow.triggers[triggerkey]; + + if (cy !== undefined && cy !== null) { + var node = cy.getElementById(item.id); + if (node.length > 0) { + if (inside) { + node.addClass("shuffle-hover-highlight"); + } else { + node.removeClass("shuffle-hover-highlight"); + } + } + } + } + } + } + + const handleActionHover = (inside, actionId) => { + if (cy !== undefined && cy !== null) { + var node = cy.getElementById(actionId); + if (node.length > 0) { + if (inside) { + node.addClass("shuffle-hover-highlight"); + } else { + node.removeClass("shuffle-hover-highlight"); + } + } + } + }; + + const handleMouseover = () => { + if (innerdata.type === "Runtime Argument") { + handleExecArgumentHover(true); + } else if (innerdata.type === "action") { + handleActionHover(true, innerdata.id); + } + }; + + const handleMouseOut = () => { + if (innerdata.type === "Runtime Argument") { + handleExecArgumentHover(false); + } else if (innerdata.type === "action") { + handleActionHover(false, innerdata.id); + } + }; + + var parsedPaths = []; + + if (innerdata.type === "workflow_variable") { + // Try to parse the value if it's a string that could be JSON + if (typeof innerdata.value === "string") { + try { + const parsedValue = JSON.parse(innerdata.value) + if (typeof parsedValue === "object") { + parsedPaths = GetParsedPaths(parsedValue, ""); + } + } catch (e) { + // Not valid JSON, use the value directly + parsedPaths = GetParsedPaths(innerdata.value, ""); + } + } else if (typeof innerdata.value === "object") { + parsedPaths = GetParsedPaths(innerdata.value, ""); + } + } else if (typeof innerdata.example === "object") { + parsedPaths = GetParsedPaths(innerdata.example, ""); + } + + const coverColor = "#82ccc3" + + return parsedPaths.length > 0 ? ( + + {/* + + {icon} {innerdata.name} +
+ } + parentMenuOpen={!!menuPosition} + style={{ + backgroundColor: theme.palette.inputColor, + color: "white", + minWidth: 250, + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + {parsedPaths.map((pathdata, index) => { + // FIXME: Should be recursive in here + const icon = + pathdata.type === "value" ? ( + + ) : pathdata.type === "list" ? ( + + ) : ( + + ) + + return ( + { }} + onClick={() => { + handleItemClick([innerdata, pathdata]); + }} + > + +
+ {icon} {pathdata.name} +
+
+
+ ); + })} + + */} + + + {icon} {innerdata.name} +
+ } + parentMenuOpen={!!menuPosition} + style={{ + color: "white", + minWidth: 250, + maxWidth: 250, + maxHeight: 50, + overflow: "hidden", + }} + onClick={() => { + console.log(innerdata.example) + handleItemClick([innerdata]); + }} + > + + + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + + {innerdata.name} + + + + {parsedPaths.map((pathdata, index) => { + // FIXME: Should be recursive in here + // + const icon = + pathdata.type === "value" ? ( + + ) : pathdata.type === "list" ? ( + + ) : ( + + ); + // + + const indentation_count = (pathdata.name.match(/\./g) || []).length + 1 + const baseIndent =
+ //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 + const boxPadding = 0 + const namesplit = pathdata.name.split(".") + const newname = namesplit[namesplit.length - 1] + return ( + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata, pathdata]); + }} + > + +
+ {Array(indentation_count).fill().map((subdata, subindex) => { + return ( + baseIndent + ) + })} + {icon} {newname} + {pathdata.type === "list" ? { + + }} /> : null} +
+
+
+ ); + })} + + + + ) : ( + handleMouseover()} + onMouseOut={() => { + handleMouseOut(); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + +
+ {icon} {innerdata.name} +
+
+
+ ); + })} + + }
{ src={validate.result} theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} - collapsed={false} shouldCollapse={(jsonField) => { return collapseField(jsonField) }} @@ -20186,7 +20600,6 @@ const AngularWorkflow = (defaultprops) => { src={validate.result} theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} - collapsed={parsedCollapse} shouldCollapse={(jsonField) => { return collapseField(jsonField) }} @@ -21562,7 +21975,6 @@ const AngularWorkflow = (defaultprops) => { src={validate.result} theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} - collapsed={false} shouldCollapse={(jsonField) => { return collapseField(jsonField) }} @@ -21724,7 +22136,6 @@ const AngularWorkflow = (defaultprops) => { src={checked.result} theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} - collapsed={data.value.length < 10000 ? false : true} shouldCollapse={(jsonField) => { return collapseField(jsonField) }} @@ -22150,7 +22561,6 @@ const AngularWorkflow = (defaultprops) => { src={validate.result} theme={theme.palette.jsonTheme} style={theme.palette.reactJsonStyle} - collapsed={selectedResult.result.length < 10000 ? false : true} shouldCollapse={(jsonField) => { return collapseField(jsonField) }} @@ -22211,6 +22621,7 @@ const AngularWorkflow = (defaultprops) => {
)} +
{selectedResult.action.parameters !== null && selectedResult.action.parameters !== undefined ? ( diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index e910417c..9b347eae 100644 --- a/frontend/src/views/Apps2.jsx +++ b/frontend/src/views/Apps2.jsx @@ -510,7 +510,6 @@ const Hits = ({ fontFamily: theme?.typography?.fontFamily }} onClick={() => { - console.log("App modal", data) handleAppClick(data); }} > @@ -1299,6 +1298,8 @@ const Apps2 = (props) => { //console.log("Failed to get apps from localstorage: ", e) } + setIsLoading(true); + fetch(globalUrl + "/api/v1/apps", { method: "GET", headers: { From dfe0314d0405c193570f37f70b3ed467362deeb2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 12:01:29 +0100 Subject: [PATCH 031/200] Fixed dashboardchart issue --- frontend/src/views/DashboardViews.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/views/DashboardViews.jsx b/frontend/src/views/DashboardViews.jsx index 4bf532a6..7905a21a 100644 --- a/frontend/src/views/DashboardViews.jsx +++ b/frontend/src/views/DashboardViews.jsx @@ -8,7 +8,7 @@ import { useNavigate, Link, useParams } from "react-router-dom"; import { ToastContainer, toast } from "react-toastify" import Draggable from "react-draggable"; -// import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; +import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; import { Autocomplete, From 91268ca3b0d675ffc556346fa3de3710f211ec04 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 12:02:27 +0100 Subject: [PATCH 032/200] Fixed go.mod bumps --- backend/go-app/go.mod | 2 +- functions/onprem/orborus/go.mod | 2 +- functions/onprem/worker/go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index f9094264..d554503d 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.3 + github.com/shuffle/shuffle-shared v0.8.4 golang.org/x/crypto v0.32.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.68.1 diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index b7f9b684..f81e87a4 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -9,7 +9,7 @@ require ( github.com/docker/docker v27.5.0+incompatible github.com/docker/go-connections v0.5.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.3 + github.com/shuffle/shuffle-shared v0.8.4 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 ) diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 64929892..dd892f78 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -8,7 +8,7 @@ require ( github.com/docker/docker v27.5.0+incompatible github.com/gorilla/mux v1.8.1 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.3 + github.com/shuffle/shuffle-shared v0.8.4 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 k8s.io/client-go v0.30.2 From 43e28c355f36399f9f8ea43681c6323ebf6caa38 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 12:04:16 +0100 Subject: [PATCH 033/200] Fixed issues with nightly/latest and versions --- docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b3c4dbdb..adf6b533 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: frontend: - image: ghcr.io/shuffle/shuffle-frontend:nightly + image: ghcr.io/shuffle/shuffle-frontend:latest container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -14,7 +14,7 @@ services: depends_on: - backend backend: - image: ghcr.io/shuffle/shuffle-backend:nightly + image: ghcr.io/shuffle/shuffle-backend:latest container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} # Here for debugging: @@ -33,7 +33,7 @@ services: - SHUFFLE_FILE_LOCATION=/shuffle-files restart: unless-stopped orborus: - image: ghcr.io/shuffle/shuffle-orborus:nightly + image: ghcr.io/shuffle/shuffle-orborus:latest container_name: shuffle-orborus hostname: shuffle-orborus networks: @@ -55,13 +55,13 @@ services: - SHUFFLE_STATS_DISABLED=true - SHUFFLE_LOGS_DISABLED=true - SHUFFLE_SWARM_CONFIG=run - - SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:nightly + - SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:latest env_file: .env restart: unless-stopped security_opt: - seccomp:unconfined opensearch: - image: opensearchproject/opensearch:2.14.0 + image: opensearchproject/opensearch:2.19.1 hostname: shuffle-opensearch container_name: shuffle-opensearch environment: From af30feec84c7df385bb58d7a2c05b06b7658aa1a Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 12:13:49 +0100 Subject: [PATCH 034/200] Update dockerbuild.yaml --- .github/workflows/dockerbuild.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index a04e87bd..570c21f1 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: branches: - - nightly + - main paths: - "**" - "!.github/**" @@ -20,19 +20,19 @@ jobs: include: - app: frontend path: frontend - version: nightly + version: latest experimental: true - app: backend path: backend - version: nightly + version: latest experimental: true - app: orborus path: functions/onprem/orborus - version: nightly + version: latest experimental: true - app: worker path: functions/onprem/worker - version: nightly + version: latest experimental: true steps: - name: Checkout From 3d02907947cb1e9bfa507a5c3ed95fb81e02e441 Mon Sep 17 00:00:00 2001 From: Piyush Kumar Pradhan Date: Mon, 3 Mar 2025 16:59:11 +0530 Subject: [PATCH 035/200] removed the linting --- frontend/src/defaultCytoscapeStyle.jsx | 31 ++- frontend/src/views/AngularWorkflow.jsx | 305 +++++++++++++++++++++++-- 2 files changed, 317 insertions(+), 19 deletions(-) diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index d7a6b68e..723ad938 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -95,23 +95,42 @@ const data = [ { selector: `node[type="COMMENT"]`, css: { - label: function(element) { - return element.data("label") - }, + label: function (element) { + return element.data("label") + }, shape: "roundrectangle", color: "data(color)", width: "data(width)", height: "data(height)", - padding: "0px", + padding: "5px", margin: "0px", "background-color": "data(backgroundcolor)", "background-image": "data(backgroundimage)", "border-color": "#ffffff", - "text-margin-x": "0px", + "text-margin-x": "data(textMarginX)", + "text-margin-y": "data(textMarginY)", "z-index": 4999, "border-radius": "5px", "background-opacity": "0.5", - "text-wrap": "wrap", + "text-wrap": "wrap", + "text-max-width": "data(width)", + "text-halign": "data(textHalign)", + "text-valign": "data(textValign)" + }, + }, + { + selector: `node[type="RESIZE-HANDLE"]`, + css: { + shape: "ellipse", + width: "8px", + height: "8px", + "border-width": 1, + "border-color": "white", + "z-index": 5002, + "overlay-opacity": 0, + "cursor": "nwse-resize", + "opacity": 0, + "pointer-events": "auto", }, }, { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 16d5e9d0..37e58463 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1074,6 +1074,7 @@ const AngularWorkflow = (defaultprops) => { // Special multi-workflow edgecase handler for events if (distributedFromParent === "" && suborgWorkflows === []) { } else { + console.log("here"); if (cy !== undefined && cy !== null) { cy.removeListener("select"); cy.removeListener("unselect"); @@ -1118,7 +1119,8 @@ const AngularWorkflow = (defaultprops) => { !el.data("isButton") && !el.data("isDescriptor") && !el.data("isSuggestion") && - el.data("type") !== "COMMENT") { + el.data("type") !== "COMMENT" && + el.data("type") !== "RESIZE-HANDLE") { return true } @@ -2325,6 +2327,7 @@ const AngularWorkflow = (defaultprops) => { var newBranches = []; var newVBranches = []; var newComments = []; + var newResizes = []; for (let cyelementsKey in cyelements) { if (cyelements[cyelementsKey].data === undefined) { continue; @@ -2509,7 +2512,40 @@ const AngularWorkflow = (defaultprops) => { //console.log(curworkflowComment) newComments.push(curworkflowComment); - } else { + } else if (type === "RESIZE-HANDLE") { + if (useworkflow.resizes === undefined || useworkflow.resizes === null) { + useworkflow.resizes = []; + } + + var curworkflowResize = useworkflow.resizes.find( + (a) => a.id === cyelements[cyelementsKey].data()["id"] + ); + + if (curworkflowResize === undefined) { + curworkflowResize = cyelements[cyelementsKey].data(); + } + + // Ensure width and height are properly parsed + const parsedHeight = parseInt(curworkflowResize["height"]); + if (!isNaN(parsedHeight)) { + curworkflowResize.height = parsedHeight; + } else { + curworkflowResize.height = 150; // Default value if parsing fails + } + + const parsedWidth = parseInt(curworkflowResize["width"]); + if (!isNaN(parsedWidth)) { + curworkflowResize.width = parsedWidth; + } else { + curworkflowResize.width = 200; // Default value if parsing fails + } + + // Update position from Cytoscape + curworkflowResize.position = cyelements[cyelementsKey].position(); + + newResizes.push(curworkflowResize); + } + else { toast("No handler for type: " + type); } } @@ -6522,6 +6558,21 @@ const AngularWorkflow = (defaultprops) => { } setSelectedComment(data); + } else if (data.type === "RESIZE-HANDLE") { + + const parentNode = cy.getElementById(data.attachedTo); + if (parentNode) { + console.log("Resizing parent node:", parentNode); + + // Get the parent node's data + const parentData = parentNode.data(); + if (parentData?.type === "COMMENT") { + + // Set the parent node's data as the selected comment + setSelectedComment(parentData); + } + } + return; // Exit after handling the resize handle } else { toast("Can't handle node type " + data.type); return; @@ -9650,7 +9701,8 @@ const AngularWorkflow = (defaultprops) => { !el.data("isButton") && !el.data("isDescriptor") && !el.data("isSuggestion") && - el.data("type") !== "COMMENT") { + el.data("type") !== "COMMENT" && + el.data("type") !== "RESIZE-HANDLE") { return true } @@ -9732,6 +9784,27 @@ const AngularWorkflow = (defaultprops) => { cy.on("mouseover", "node", (e) => onNodeHover(e)); cy.on("mouseout", "node", (e) => onNodeHoverOut(e)); + cy.on("mouseover", "node[type='RESIZE-HANDLE']", (e) => { + const nodeId = e.target.id(); + + // Check the node ID to determine the cursor style based on position + if (nodeId.includes("bottom-right")) { + cy.container().style.cursor = "nwse-resize"; // Bottom-right resize cursor + } else if (nodeId.includes("top-right")) { + cy.container().style.cursor = "nesw-resize"; // Top-right resize cursor + } else if (nodeId.includes("top-left")) { + cy.container().style.cursor = "nwse-resize"; // Top-left resize cursor + } else if (nodeId.includes("bottom-left")) { + cy.container().style.cursor = "nesw-resize"; // Bottom-left resize cursor + } else { + cy.container().style.cursor = "default"; // Default cursor for other cases + } + }); + + cy.on("mouseout", "node[type='RESIZE-HANDLE']", (e) => { + cy.container().style.cursor = ""; + }); + // Handles dragging cy.on("drag", "node", (e) => onNodeDrag(e, selectedAction)); cy.on("free", "node", (e) => onNodeDragStop(e, selectedAction)); @@ -15699,6 +15772,79 @@ const AngularWorkflow = (defaultprops) => { setSelectedComment(selectedComment); }} /> +
+
+
Justify
+ +
+
+
Align
+ +
+
Height
@@ -15803,7 +15949,6 @@ const AngularWorkflow = (defaultprops) => { defaultValue={selectedComment["backgroundimage"]} onChange={(event) => { selectedComment.backgroundimage = event.target.value; - console.log("Comment: ", selectedComment) setSelectedComment(selectedComment); }} /> @@ -19696,12 +19841,115 @@ const AngularWorkflow = (defaultprops) => { ); }; + const setupResizeHandlers = (cy, nodeId) => { + let height; + let width; + let resizeTimeout; + + cy.on("drag", ".resize-handle", (event) => { + if (resizeTimeout) return; + + resizeTimeout = setTimeout(() => { + resizeTimeout = null; + const handle = event.target; + const parent = cy.$(`#${nodeId}`); // Fetch the main node directly + + // Check if the parent node exists + if (!parent || parent.empty()) { + console.warn(`Parent node (${nodeId}) not found.`); + return; + } + + if (!handle?.position()) { + console.warn(`Handle position is undefined for ${handle.id()}`); + return; + } + + const handlePos = handle.position(); + const parentPos = parent.position(); + + if (!parentPos) { + console.warn(`Parent position is undefined for ${nodeId}`); + return; + } + + // Calculate new width & height based on handle movement + const newWidth = Math.abs(handlePos.x - parentPos.x) * 2; + const newHeight = Math.abs(handlePos.y - parentPos.y) * 2; + + // Apply min/max constraints + const constrainedWidth = Math.max(100, Math.min(newWidth, 500)); + const constrainedHeight = Math.max(50, Math.min(newHeight, 300)); + + // Update node size + parent.style({ + width: constrainedWidth, + height: constrainedHeight, + }); + + // Store dimensions for state update on drag end + height = Math.floor(constrainedHeight); + width = Math.floor(constrainedWidth); + + // Update handle positions + cy.$(".resize-handle").forEach((corner) => { + if (!corner?.id() || !corner.position()) return; + + const { x, y } = parent.position(); + const offsetX = corner.id().includes("left") ? -constrainedWidth / 2 : constrainedWidth / 2; + const offsetY = corner.id().includes("top") ? -constrainedHeight / 2 : constrainedHeight / 2; + + corner.position({ x: x + offsetX, y: y + offsetY }); + }); + }, 16); // Throttle to ~60 FPS + }); + + // Update state when resizing ends + cy.on("free", ".resize-handle", (event) => { + const data = event.target.data(); + const parentNode = cy.getElementById(data.attachedTo); + + if (parentNode) { + + parentNode.data({ + ...parentNode.data(), + width: Math.floor(width), + height: Math.floor(height), + }); + setSelectedComment((prev) => ({ + ...prev, + width: Math.floor(width), + height: Math.floor(height), + })); + } + }); + + }; + + + const setupNodeDragHandler = (cy, nodeId) => { + cy.on("drag", `#${nodeId}`, (event) => { + const node = event.target; + const { x, y } = node.position(); + const width = parseFloat(node.style("width")); + const height = parseFloat(node.style("height")); + + // Move resize handles with the node + cy.$(".resize-handle").forEach((corner) => { + const offsetX = corner.id().includes("left") ? -width / 2 : width / 2; + const offsetY = corner.id().includes("top") ? -height / 2 : height / 2; + + corner.position({ x: x + offsetX, y: y + offsetY }); + }); + }); + }; + const addCommentNode = () => { const newId = uuidv4(); - const position = { - x: 300, - y: 300, - }; + const position = { x: 300, y: 300 }; + const width = 250; + const height = 150; + const handleOffset = 10; // Move handles outside the node cy.add({ group: "nodes", @@ -19711,20 +19959,51 @@ const AngularWorkflow = (defaultprops) => { type: "COMMENT", is_valid: true, decorator: true, - width: 250, - height: 150, - position: position, + width, + height, + position, backgroundcolor: "#1f2023", color: "#ffffff", + textHalign: "center", + textValign: "center", + textMarginX: "0px", + textMarginY: "0px", }, - position: position, + position, }); + + // Define corner positions relative to the main node + const corners = [ + { id: `${newId}-top-left`, dx: -width / 2 - handleOffset, dy: -height / 2 - handleOffset }, + { id: `${newId}-top-right`, dx: width / 2 + handleOffset, dy: -height / 2 - handleOffset }, + { id: `${newId}-bottom-left`, dx: -width / 2 - handleOffset, dy: height / 2 + handleOffset }, + { id: `${newId}-bottom-right`, dx: width / 2 + handleOffset, dy: height / 2 + handleOffset }, + ]; + + // Add resize handles **without** the parent property + corners.forEach((corner) => { + cy.add({ + group: "nodes", + data: { + id: corner.id, + type: "RESIZE-HANDLE", + is_valid: true, + attachedTo: newId, + decorator: true, + }, // No parent to avoid edges + position: { x: position.x + corner.dx, y: position.y + corner.dy }, + classes: "resize-handle", + }); + }); + + setupResizeHandlers(cy, newId); + setupNodeDragHandler(cy, newId); }; const RightSideBar = (props) => { var defaultReturn = null - if (Object.getOwnPropertyNames(selectedComment).length > 0) { + if (Object.getOwnPropertyNames(selectedComment).length > 0 && selectedComment.hasOwnProperty('id')) { defaultReturn = } else if (Object.getOwnPropertyNames(selectedTrigger).length > 0) { if (selectedTrigger.trigger_type === undefined) { From 1e1cfec75eaad6011a3211587fc4bfbc83efdedd Mon Sep 17 00:00:00 2001 From: Piyush Kumar Pradhan Date: Mon, 3 Mar 2025 17:08:35 +0530 Subject: [PATCH 036/200] emove debug console logs. --- frontend/src/views/AngularWorkflow.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 37e58463..fbcf5552 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1074,7 +1074,6 @@ const AngularWorkflow = (defaultprops) => { // Special multi-workflow edgecase handler for events if (distributedFromParent === "" && suborgWorkflows === []) { } else { - console.log("here"); if (cy !== undefined && cy !== null) { cy.removeListener("select"); cy.removeListener("unselect"); From 2e73f772e60990cd494a99e4335abe5294d88456 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 16:38:33 +0100 Subject: [PATCH 037/200] Migrated a bunch of pages --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 4 + backend/go-app/main.go | 22 +- frontend/src/App.jsx | 26 +- frontend/src/components/AppAuthTab.jsx | 38 +- frontend/src/components/AppCreationModal.jsx | 3 +- frontend/src/components/EnvironmentTab.jsx | 2 +- frontend/src/components/TenantsTab.jsx | 6 +- frontend/src/components/UserManagmentTab.jsx | 4 +- frontend/src/views/AngularWorkflow.jsx | 4 +- frontend/src/views/AppCreator.jsx | 8 +- frontend/src/views/AppExplorer.jsx | 9 +- frontend/src/views/Apps2.jsx | 8 +- frontend/src/views/LoginPage.jsx | 1404 +++++++++++------- frontend/src/views/LoginPageOld.jsx | 546 +++++++ 15 files changed, 1514 insertions(+), 572 deletions(-) create mode 100755 frontend/src/views/LoginPageOld.jsx diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index d554503d..37174871 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.4 + github.com/shuffle/shuffle-shared v0.8.5 golang.org/x/crypto v0.32.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.68.1 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 5135bcff..8e367a5a 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -350,6 +350,10 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdR github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shuffle/shuffle-shared v0.8.4 h1:R/A62IzHJXnhVNG1PqFXN1YtRgdiiSIL5q4YJEef5P8= +github.com/shuffle/shuffle-shared v0.8.4/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= +github.com/shuffle/shuffle-shared v0.8.5 h1:c98AMOzIrDXmgQrKyf1HJ9wZwAwB02LPRvf9+yy05OQ= +github.com/shuffle/shuffle-shared v0.8.5/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index dfdce781..42b58256 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3295,6 +3295,8 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s api.Contributors = append(api.Contributors, user.Id) } + shuffle.SetAppRevision(ctx, api) + log.Printf("[INFO] API LENGTH FOR %s: %d, ID: %s", api.Name, len(parsed.Body), newmd5) // FIXME: Might cause versioning issues if we re-use the same!! // FIXME: Need a way to track different versions of the same app properly. @@ -3365,11 +3367,27 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s } } + log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID) if len(user.Id) > 0 { resp.WriteHeader(200) resp.Write([]byte(fmt.Sprintf(`{"success": true, "id": "%s"}`, api.ID))) } + + org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) + if err != nil { + log.Printf("[ERROR] Failed getting org during image build (%s): %s", user.ActiveOrg.Id, err) + } else { + imagenames := []string{ + fmt.Sprintf("%s_%s", api.Name, api.AppVersion), + fmt.Sprintf("%s_%s", api.Name, api.ID), + } + + err = shuffle.DistributeAppToEnvironments(ctx, *org, imagenames) + if err != nil { + log.Printf("[ERROR] Failed distributing app to environments: %s", err) + } + } } // Creates an app from the app builder @@ -4359,8 +4377,8 @@ func runInitEs(ctx context.Context) { } if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" { - healthcheckInterval := 30 - log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval) + healthcheckInterval := 60 + log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats, and dashboard on /health. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval) job := func() { // Prepare a fake http.responsewriter resp := httptest.NewRecorder() diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index b6f3e6b0..9fc34790 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -34,9 +34,10 @@ import RunWorkflow from "./views/RunWorkflow.jsx"; import Admin2 from "./views/Admin2.jsx"; import LoginPage from "./views/LoginPage.jsx"; +//import LoginPage from "./views/LoginPage.jsx"; + import SettingsPage from "./views/SettingsPage.jsx"; import KeepAlive from "./views/KeepAlive.jsx"; - import { ThemeProvider } from "@mui/material/styles"; import CssBaseline from '@mui/material/CssBaseline'; @@ -236,6 +237,7 @@ const App = (message, props) => {
*/} + { { /> } /> + + + } + /> + {
- + + {isCloud ? + + : null}
{/* { {editAuthenticationModal} {authenticationView}
+ + {/*

App Authentication Groups

@@ -1415,13 +1420,14 @@ const AppAuthTab = memo((props) => { ); } )} - -
- -
-
-
- + + + + + */} + + + ); }); diff --git a/frontend/src/components/AppCreationModal.jsx b/frontend/src/components/AppCreationModal.jsx index f5a25169..80cf1711 100644 --- a/frontend/src/components/AppCreationModal.jsx +++ b/frontend/src/components/AppCreationModal.jsx @@ -59,6 +59,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { : "1px solid rgba(255,255,255,0.3)", borderImage: makeFancy ? "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1" : "none", transition: 'all 0.2s ease-in-out', + paddingBottom: isCloud ? 0 : 175, } return ( @@ -416,7 +417,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { -
+
{ diff --git a/frontend/src/components/EnvironmentTab.jsx b/frontend/src/components/EnvironmentTab.jsx index cd0a9416..15efd2fc 100644 --- a/frontend/src/components/EnvironmentTab.jsx +++ b/frontend/src/components/EnvironmentTab.jsx @@ -1420,7 +1420,7 @@ const EnvironmentTab = memo((props) => { -
+
Self-Hosted Orborus instance diff --git a/frontend/src/components/TenantsTab.jsx b/frontend/src/components/TenantsTab.jsx index 0d8c3df2..afeb9563 100644 --- a/frontend/src/components/TenantsTab.jsx +++ b/frontend/src/components/TenantsTab.jsx @@ -497,7 +497,9 @@ const TenantsTab = memo((props) => { response.json().then((responseJson) => { if (responseJson["success"] === false) { if (responseJson.reason !== undefined) { - toast(responseJson.reason); + toast.error(responseJson.reason, { + autoClose: 5000, + }) } else { toast("Failed creating suborg. Please try again"); } @@ -781,7 +783,7 @@ const TenantsTab = memo((props) => {
-

Organizations

+

Tenants

Control sub organizations (tenants)! {" "} {isCloud diff --git a/frontend/src/components/UserManagmentTab.jsx b/frontend/src/components/UserManagmentTab.jsx index 2bb924b0..8aff24a5 100644 --- a/frontend/src/components/UserManagmentTab.jsx +++ b/frontend/src/components/UserManagmentTab.jsx @@ -1557,10 +1557,12 @@ const UserManagmentTab = memo((props) => { style={{ display:'table-cell', verticalAlign: 'middle' }} /> + {/* + */} { style={{ display:'table-cell',verticalAlign: 'middle', padding: "8px", }} /> - {/* { } style={{ display:'table-cell', verticalAlign: 'middle',padding: "8px", color: data.mfa_info.active ? "#02CB70" : "#F53434" }} /> - */} {selectedOrganization?.child_orgs !== undefined && selectedOrganization?.child_orgs !== null && diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 16d5e9d0..51812543 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -11446,7 +11446,7 @@ const AngularWorkflow = (defaultprops) => { var type = "app" const baseImage = return ( -
+
{hits.length === 0 ? @@ -11736,7 +11736,7 @@ const AngularWorkflow = (defaultprops) => { }} > - Click one of the relevant public apps below to Activate it for your organization. + Click one of the public apps below to Activate it for your organization. { console.log("CLICKED") diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 2d1bbb32..50106831 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -2657,12 +2657,12 @@ const AppCreator = (defaultprops) => { if (responseJson.reason !== undefined) { setErrorCode(responseJson.reason); - if (responseJson.extra === undefined && responseJson.extra === null) { - toast("Failed to verify: " + responseJson.reason); - } + toast.error("Failed to build: " + responseJson.reason, { + autoClose: 10000 + }) } } else { - toast("Successfully uploaded openapi"); + toast.success("Successfully built openapi app! Added job to rebuild it in your hybrid runtime locations (Orborus)."); if (window.location.pathname.includes("/new")) { if (responseJson.id !== undefined && responseJson.id !== null) { window.location = `/apps/edit/${responseJson.id}`; diff --git a/frontend/src/views/AppExplorer.jsx b/frontend/src/views/AppExplorer.jsx index b3cb0d99..d37709ee 100644 --- a/frontend/src/views/AppExplorer.jsx +++ b/frontend/src/views/AppExplorer.jsx @@ -3866,13 +3866,13 @@ const AppExplorer = (props) => { } }} > - +
Are you sure you want to PUBLISH this app?
@@ -4120,7 +4120,7 @@ const AppExplorer = (props) => { )} {appType === 1 ? null : - creatorProfile.self === true || userdata.support === true ? + userdata?.support || userdata?.id === app?.owner || (userdata?.admin === "true" && userdata?.active_org?.id === app?.reference_org) || app?.contributors?.includes(userdata?.id) || creatorProfile?.self === true ? - {(appdata?.owner === userdata?.id) ? ( - ): null }
) : ( - {hoverEffect === index && isCloud ? ( + {hoverEffect === index ? (
{data.tags && ( { { }} /> { { - const { - globalUrl, - isLoaded, - isLoggedIn, - setIsLoggedIn, - setCookie, - register, - checkLogin, - } = props; - let navigate = useNavigate(); - const classes = useStyles(); +const FreePlanCard = ({ classes }) => { + const features = [ + "Access to All Apps", + "10,000 App Runs", + "Monthly Runs Refresh", + "Unlimited Users & Workflows", + "Multi-Tenancy & -Region", + "Support & Discord Access" + ]; - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [firstRequest, setFirstRequest] = useState(true); - const [loginLoading, setLoginLoading] = useState(false); - const [loginViewLoading, setLoginViewLoading] = useState(false); - const [ssoUrl, setSSOUrl] = useState(""); + return ( +
+

+ The free plan includes: +

- const [MFAField, setMFAField] = useState(false); - const [MFAValue, setMFAValue] = useState(""); - - - // Used to swap from login to register. True = login, false = register - - useEffect(() => { - checkAdmin() - }, [loginViewLoading]) - - // Error messages etc - const [loginInfo, setLoginInfo] = useState(""); - - const handleValidateForm = () => { - return username.length > 1 && password.length > 1; - }; - - if (isLoggedIn === true) { - //window.location.pathname = "/workflows"; - navigate("/workflows") - } - - const checkAdmin = () => { - const url = globalUrl + "/api/v1/checkusers"; - fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => - response.json().then((responseJson) => { - if (responseJson["success"] === false) { - setLoginInfo(responseJson["reason"]); - } else { - - if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { - setSSOUrl(responseJson.sso_url); - } - - if (loginViewLoading) { - setLoginViewLoading(false); - checkLogin(); - stop(); - - if ( - responseJson.reason !== undefined && - responseJson.reason !== null - ) { - setLoginInfo(responseJson.reason); - } - } - - if (responseJson.reason === "stay") { - navigate("/adminsetup") - } - } - }) - ) - .catch((error) => { - if (!loginViewLoading) { - setLoginViewLoading(true); - start(); - } - }); - }; - - const { start, stop } = useInterval({ - duration: 3000, - startImmediate: false, - callback: () => { - checkAdmin(); - }, - }); - - if (firstRequest) { - setFirstRequest(false); - checkAdmin(); - } - - const onSubmit = (e) => { - setLoginLoading(true); - e.preventDefault(); - setLoginInfo(""); - // FIXME - add some check here ROFL - - // Just use this one? - var data = { username: username, password: password }; - if (MFAValue !== undefined && MFAValue !== null && MFAValue.length > 0) { - data["mfa_code"] = MFAValue; - } - - var baseurl = globalUrl; - if (register) { - var url = baseurl + "/api/v1/login"; - fetch(url, { - mode: "cors", - method: "POST", - body: JSON.stringify(data), - credentials: "include", - crossDomain: true, - withCredentials: true, - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - }) - .then((response) => - response.json().then((responseJson) => { - setLoginLoading(false); - if (responseJson["success"] === false) { - setLoginInfo(responseJson["reason"]); - } else { - if (responseJson["reason"] === "MFA_REDIRECT") { - setLoginInfo( - "MFA required. Please enter the 6-digit code from your authenticator" - ); - setMFAField(true); - return; - } else if (responseJson["reason"] === "MFA_SETUP") { - window.location.href = `/login/${responseJson.url}/mfa-setup`; - return; - } - - setLoginInfo("Successful login, rerouting"); - for (var key in responseJson["cookies"]) { - setCookie( - responseJson["cookies"][key].key, - responseJson["cookies"][key].value, - { path: "/" } - ); - } - - if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) { - console.log("RUN Welcome!!") - setTimeout(() => { - navigate("/welcome?tab=2") - },200) - // window.location.pathname = "" - return - } - - const tmpView = new URLSearchParams(window.location.search).get("view") - if (tmpView !== undefined && tmpView !== null) { - //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}` - const newUrl = `/${tmpView}` - window.location.pathname = newUrl - } else { - window.location.pathname = "/workflows" - } - - setIsLoggedIn(true); - } - }) - ) - .catch((error) => { - setLoginLoading(false); - setLoginInfo("Error logging in: " + error); - }); - } else { - url = baseurl + "/api/v1/users/register"; - fetch(url, { - method: "POST", - body: JSON.stringify(data), - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => - response.json().then((responseJson) => { - if (responseJson["success"] === false) { - setLoginInfo(responseJson["reason"]); - } else { - setLoginInfo("Successful register!"); - } - }) - ) - .catch((error) => { - setLoginInfo("Error in from backend: ", error); - }); - } - }; - - const onChangeUser = (e) => { - setUsername(e.target.value); - }; - - const onChangePass = (e) => { - setPassword(e.target.value); - }; - - //const onClickRegister = () => { - // if (props.location.pathname === "/login") { - // window.location.pathname = "/register" - // } else { - // window.location.pathname = "/login" - // } - - // setLoginCheck(!register) - //} - - //var loginChange = register ? (

Want to register? Click here.

) : (

Go back to login? Click here.

); - var formtitle = register ?
Login
:
Register
; - const imgsize = 100; - const basedata = ( -
- -
- -
- - {loginViewLoading ? ( -
- - Waiting for the Shuffle database to become available. This may - take up to a minute. - - - {loginInfo === undefined || - loginInfo === null || - loginInfo.length === 0 ? null : ( -
Database Response: {loginInfo}
- )} - - - - - - Are you sure Shuffle is{" "} -
- installed correctly - - ? - - - - 1. Make sure shuffle-database folder has correct access, and that you have a minimum of 2Gb of RAM available:{" "} -
-
- sudo chown -R 1000:1000 shuffle-database -
- - 2. Disable memory swap on the host: -
-
- sudo swapoff -a -
- - 3. Restart the database: -
-
- sudo docker restart shuffle-opensearch -
- - - Need help?{" "} - - Join the Discord! - - -
- ) : ( -
-

{formtitle}

- Username -
- -
- Password -
- -
- {MFAField === true ? ( -
- 2-factor code - { - setMFAValue(event.target.value); - }} - /> -
- ) : null} -
- -
-
{loginInfo}
- {ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? ( -
- Or -
- -
-
- ) : null} -
- )} - -
- ); - - const loadedCheck = isLoaded ?
{basedata}
:
; - - useEffect(() => { - setTimeout(() => { - if (ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0) { - //id="sso_button" - const ssoBtn = document.getElementById("sso_button"); - if (ssoBtn !== undefined && ssoBtn !== null) { - //console.log("SSO BTN: ", ssoBtn) - const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; - var tmpView = new URLSearchParams(cursearch).get("autologin"); - if (tmpView !== undefined && tmpView !== null) { - if (tmpView === "true") { - console.log("Tmp: ", tmpView) - ssoBtn.click() - } - } - } - } - }, 200); - }, [ssoUrl]) - - return
{loadedCheck}
; + {features.map((feature, index) => ( +
+ + {feature} +
+ ))} +
+ ); }; -export default LoginDialog; +const MarketplaceCard = ({ classes }) => { + const marketplaceOptions = [ + { + name: "Amazon Web Services", + logo: "https://cdn.cdnlogo.com/logos/a/19/aws.svg", + tooltipText: "Coming soon to AWS Marketplace!", + valid: false, + }, + { + name: "Microsoft Azure", + logo: "https://cdn.cdnlogo.com/logos/a/12/azure.svg", + tooltipText: "Coming soon to Azure Marketplace!", + valid: false, + }, + { + name: "Google Cloud Platform", + logo: "https://cdn.cdnlogo.com/logos/g/75/google-cloud.svg", + tooltipText: "Coming soon to Google Cloud Marketplace!", + valid: false, + } + ]; + + return ( +
+

+ Self Host +

+ + {marketplaceOptions.map((option, index) => ( + + + + ))} + + + +
+ ); +}; + + +const LoginPage = props => { + const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, inregister, serverside } = props; + let navigate = useNavigate(); + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [message, setMessage] = useState(""); + const [loginLoading, setLoginLoading] = useState(false); + + const [MFAField, setMFAField] = useState(false); + const [MFAValue, setMFAValue] = useState(""); + const [register, setRegister] = useState(inregister); + const [checkboxClicked, setCheckboxClicked] = useState(false); + const [loginWithSSO, setLoginWithSSO] = useState(false) + + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io"; + const parsedsearch = serverside === true ? "" : window.location.search + if (serverside !== true) { + const tmpMessage = new URLSearchParams(window.location.search).get("message") + if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) { + setMessage(tmpMessage) + } + } + + if (document !== undefined) { + if (register) { + document.title = "Login to Shuffle SaaS" + } else { + document.title = "Register to Shuffle SaaS" + } + } + + // Just a way to force location loading properly + // Register & login should be split :3 + if (window !== undefined) { + const path = window.location.pathname; + if (path.includes("/login") && register === false) { + console.log("Should register instead of login!") + setRegister(!register) + } else if (path.includes("/register") && register === true) { + if (!isCloud) { + setRegister(true) + navigate("/login") + } + + console.log("Should login instead of register!") + setRegister(!register) + } else { + console.log("Path: " + path, "Register: " + register) + } + } + + const bodyDivStyle = { + marginTop: 100, + width: isMobile ? "100%" : "100%", + maxWidth: "1200px", // Increased max-width to accommodate larger cards + background: "#1A1A1A", + margin: "auto", + display: isMobile ? "block" : "flex", + padding: "40px", + gap: "40px", + overflow: "hidden", + alignItems: "center" + }; + + const boxStyle = { + color: "white", + padding: "40px", + flex: 1, + maxWidth: isMobile ? "100%" : "550px", + background: "#212121", + borderRadius: "12px", + display: "flex", + // flexDirection: "column", + }; + + + + const paperStyleReg = { + width: 300, + height: 350, + background: "#212121", + borderRadius: "8px", + marginLeft: isMobile ? 80 : register ? "455px" : "485px", + marginTop: isMobile ? 10 : 110, + position: isMobile ? "" : "absolute", + + } + const paperStyleLog = { + position: "absolute", + width: isMobile ? "400px" : "532px", + padding: "0px 30px 0px", + // marginTop: "135px", + // background: "#212121", + borderRadius: "8px", + } + const createData = (icon, title) => { + return { + icon, + title, + } + } + + // Used to swap from login to register. True = login, false = register + const activeIcon = + const rows = [ + createData(activeIcon, "Access to All Apps"), + createData(activeIcon, "10,000 App Runs"), + createData(activeIcon, "Monthly Runs Refresh"), + createData(activeIcon, "Unlimited Users & Workflows"), + createData(activeIcon, "Multi-Tenancy & -Region"), + createData(activeIcon, "Support & Discord Access"), + ]; + const classes = useStyles(); + // Error messages etc + const [loginInfo, setLoginInfo] = useState(""); + + const handleValidateForm = (username, password) => { + if (loginWithSSO) { + return username.length > 1 + } + + if (!isCloud) { + return (username.length > 0 && password.length > 0); + } + + return (username.length > 1 && password.length > 8); + } + + if (isLoggedIn === true && serverside !== true) { + const tmpView = new URLSearchParams(window.location.search).get("view") + if (tmpView !== undefined && tmpView !== null && tmpView === "pricing") { + window.location.pathname = "/pricing" + return + } else if (tmpView !== undefined && tmpView !== null) { + window.location.pathname = tmpView + return + } + + window.location.pathname = "/workflows" + } + + const onSubmit = (e) => { + //toast("Testing from login page") + + setMessage("") + setLoginLoading(true) + e.preventDefault() + + // Just use this one? + var data = { "username": username, "password": password } + if (MFAValue !== undefined && MFAValue !== null && MFAValue.length > 0) { + data["mfa_code"] = MFAValue + } + + localStorage.setItem("globalUrl", "") + + var baseurl = globalUrl + if (register) { + var url = baseurl + '/api/v1/login'; + + if (loginWithSSO === true) { + url = baseurl + '/api/v1/login/sso' + setLoginInfo("Logging in with SSO. Please wait while we find a relevant org...") + } + + fetch(url, { + mode: 'cors', + method: 'POST', + body: JSON.stringify(data), + credentials: 'include', + crossDomain: true, + withCredentials: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for login:O!"); + } + + return response.json(); + }) + .then((responseJson) => { + + setLoginLoading(false) + + console.log("Resp from backend: ", responseJson) + + if (responseJson["success"] === false) { + setLoginInfo(responseJson["reason"]) + } + else { + + if (responseJson["reason"] === "MFA_REDIRECT") { + setLoginInfo("Enter the 6-digit MFA code.") + setMFAField(true) + return + + } + else if (responseJson["reason"] === "MFA_SETUP") { + window.location.href = `/login/${responseJson.url}/mfa-setup`; + return; + } + else if (responseJson["reason"] === "SSO_REDIRECT") { + //navigate(responseJson["url"]) + window.location.href = responseJson["url"] + return + + } + else if (responseJson["reason"] !== undefined && responseJson["reason"] !== null && responseJson["reason"].includes("error")) { + setLoginInfo(responseJson["reason"]) + return + } + + + setLoginInfo("Successful login! Redirecting you in 3 seconds...") + for (var key in responseJson["cookies"]) { + setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) + } + + const tmpView = new URLSearchParams(window.location.search).get("view") + if (tmpView !== undefined && tmpView !== null) { + //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}` + // Check if slash in the url + + var newUrl = `/${tmpView}` + if (tmpView.startsWith("/")) { + newUrl = `${tmpView}` + } + + console.log("Found url: ", newUrl) + + window.location.pathname = newUrl + return + } + + console.log("LOGIN DATA: ", responseJson) + if (responseJson.tutorials !== undefined && responseJson.tutorials !== null) { + // Find welcome in responseJson.tutorials under key name + const welcome = responseJson.tutorials.find(function (element) { + return element.name === "welcome"; + }) + + console.log("Welcome: ", welcome) + if (welcome === undefined || welcome === null) { + console.log("RUN login Welcome!!") + // window.location.pathname = "/welcome?tab=2" + // window.location = "/welcome?tab=2" + window.location.href = "/welcome?tab=2" + return + } + } + + window.location.pathname = "/workflows" + } + }) + .catch(error => { + setLoginInfo("Error from login API: " + error) + setLoginLoading(false) + }); + } else { + url = baseurl + '/api/v1/register'; + fetch(url, { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(response => + response.json().then(responseJson => { + if (responseJson["success"] === false) { + setLoginInfo(responseJson["reason"]) + } else { + if (responseJson["reason"] === "shuffle_account") { + window.location.href = "/login?message=Please+login+with+your+Shuffle+account" + return + } + + //setLoginInfo("Successful register!") + //var newpath = "/login?message=Successfully signed up. You can now sign in." + //const tmpMessage = new URLSearchParams(window.location.search).get("message") + setLoginInfo("Successful registration! Redirecting in 3 seconds...") + for (var key in responseJson["cookies"]) { + setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) + } + + setTimeout(() => { + console.log("LOGIN DATA: ", responseJson) + + const tmpView = new URLSearchParams(window.location.search).get("view") + if (tmpView !== undefined && tmpView !== null) { + //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}` + const newUrl = `/${tmpView}` + window.location.pathname = newUrl + return + } + + //if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) { + console.log("RUN Welcome!!") + //window.location.pathname = "/welcome?tab=2" + window.location.href = "/welcome" + }, 1500); + } + setLoginLoading(false) + }), + ) + .catch(error => { + setLoginInfo("Error in login. Please try again, or contact support@shuffler.io if the problem persists.") + setLoginLoading(false) + }); + } + } + + const onChangeUser = (e) => { + setUsername(e.target.value) + } + + const onChangePass = (e) => { + setPassword(e.target.value) + } + + const HandleLoginWithSSO = () => { + setPassword("") + setLoginInfo("") + setLoginWithSSO(true) + } + + //const onClickRegister = () => { + // if (props.location.pathname === "/login") { + // window.location.pathname = "/register" + // } else { + // window.location.pathname = "/login" + // } + + // setLoginCheck(!register) + //} + + //var loginChange = register ? (

Want to register? Click here.

) : (

Go back to login? Click here.

); + var formtitle = register ?
Welcome Back!
:
Create your account
+ var formButton = !isCloud ? "" : register ?
Don’t have an account yet?
Register here
: <> +
Already have an account?
Login here
+ + //
Click here to Login
+ + // {formtitle} + + const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)" + + const buttonStyle = { borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(username, password) || loginLoading || (checkboxClicked && register) ? buttonBackground : "grey", color: "white" } + // +
+ +
+
+ {formButton} +
+
+ +
+ {loginInfo} +
+ + + + {isMobile ? null : ( + <> +
+ OR +
+ + + )} +
+ ); + + const loadedCheck = isLoaded ? +
+ {basedata} +
+ : +
+
+ + return ( +
+ {loadedCheck} +
+ ) +} + +export default LoginPage; diff --git a/frontend/src/views/LoginPageOld.jsx b/frontend/src/views/LoginPageOld.jsx new file mode 100755 index 00000000..aaf376cd --- /dev/null +++ b/frontend/src/views/LoginPageOld.jsx @@ -0,0 +1,546 @@ +/* eslint-disable react/no-multi-comp */ +import React, { useState, useEffect } from "react"; +import { makeStyles } from "@mui/styles"; +import { useInterval } from "react-powerhooks"; +import theme from '../theme.jsx'; + +import { + CircularProgress, + TextField, + Button, + Paper, + Typography, +} from "@mui/material"; + +import { useNavigate } from "react-router-dom"; + +const hrefStyle = { + color: "white", + textDecoration: "none", +}; + +const bodyDivStyle = { + margin: "auto", + marginTop: 150, + width: "500px", +}; + +const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#f85a3e !important", + }, +}); + +const LoginDialog = (props) => { + const { + globalUrl, + isLoaded, + isLoggedIn, + setIsLoggedIn, + setCookie, + register, + checkLogin, + } = props; + + let navigate = useNavigate(); + const classes = useStyles(); + + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [firstRequest, setFirstRequest] = useState(true); + const [loginLoading, setLoginLoading] = useState(false); + const [loginViewLoading, setLoginViewLoading] = useState(false); + const [ssoUrl, setSSOUrl] = useState(""); + + const [MFAField, setMFAField] = useState(false); + const [MFAValue, setMFAValue] = useState(""); + + + // Used to swap from login to register. True = login, false = register + + useEffect(() => { + checkAdmin() + }, [loginViewLoading]) + + // Error messages etc + const [loginInfo, setLoginInfo] = useState(""); + + const handleValidateForm = () => { + return username.length > 1 && password.length > 1; + }; + + if (isLoggedIn === true) { + //window.location.pathname = "/workflows"; + navigate("/workflows") + } + + const checkAdmin = () => { + const url = globalUrl + "/api/v1/checkusers"; + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === false) { + setLoginInfo(responseJson["reason"]); + } else { + + if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { + setSSOUrl(responseJson.sso_url); + } + + if (loginViewLoading) { + setLoginViewLoading(false); + checkLogin(); + stop(); + + if ( + responseJson.reason !== undefined && + responseJson.reason !== null + ) { + setLoginInfo(responseJson.reason); + } + } + + if (responseJson.reason === "stay") { + navigate("/adminsetup") + } + } + }) + ) + .catch((error) => { + if (!loginViewLoading) { + setLoginViewLoading(true); + start(); + } + }); + }; + + const { start, stop } = useInterval({ + duration: 3000, + startImmediate: false, + callback: () => { + checkAdmin(); + }, + }); + + if (firstRequest) { + setFirstRequest(false); + checkAdmin(); + } + + const onSubmit = (e) => { + setLoginLoading(true); + e.preventDefault(); + setLoginInfo(""); + // FIXME - add some check here ROFL + + // Just use this one? + var data = { username: username, password: password }; + if (MFAValue !== undefined && MFAValue !== null && MFAValue.length > 0) { + data["mfa_code"] = MFAValue; + } + + var baseurl = globalUrl; + if (register) { + var url = baseurl + "/api/v1/login"; + fetch(url, { + mode: "cors", + method: "POST", + body: JSON.stringify(data), + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => + response.json().then((responseJson) => { + setLoginLoading(false); + if (responseJson["success"] === false) { + setLoginInfo(responseJson["reason"]); + } else { + if (responseJson["reason"] === "MFA_REDIRECT") { + setLoginInfo( + "MFA required. Please enter the 6-digit code from your authenticator" + ); + setMFAField(true); + return; + } else if (responseJson["reason"] === "MFA_SETUP") { + window.location.href = `/login/${responseJson.url}/mfa-setup`; + return; + } + + setLoginInfo("Successful login, rerouting"); + for (var key in responseJson["cookies"]) { + setCookie( + responseJson["cookies"][key].key, + responseJson["cookies"][key].value, + { path: "/" } + ); + } + + if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) { + console.log("RUN Welcome!!") + setTimeout(() => { + navigate("/welcome?tab=2") + },200) + // window.location.pathname = "" + return + } + + const tmpView = new URLSearchParams(window.location.search).get("view") + if (tmpView !== undefined && tmpView !== null) { + //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}` + const newUrl = `/${tmpView}` + window.location.pathname = newUrl + } else { + window.location.pathname = "/workflows" + } + + setIsLoggedIn(true); + } + }) + ) + .catch((error) => { + setLoginLoading(false); + setLoginInfo("Error logging in: " + error); + }); + } else { + url = baseurl + "/api/v1/users/register"; + fetch(url, { + method: "POST", + body: JSON.stringify(data), + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === false) { + setLoginInfo(responseJson["reason"]); + } else { + setLoginInfo("Successful register!"); + } + }) + ) + .catch((error) => { + setLoginInfo("Error in from backend: ", error); + }); + } + }; + + const onChangeUser = (e) => { + setUsername(e.target.value); + }; + + const onChangePass = (e) => { + setPassword(e.target.value); + }; + + //const onClickRegister = () => { + // if (props.location.pathname === "/login") { + // window.location.pathname = "/register" + // } else { + // window.location.pathname = "/login" + // } + + // setLoginCheck(!register) + //} + + //var loginChange = register ? (

Want to register? Click here.

) : (

Go back to login? Click here.

); + var formtitle = register ?
Login
:
Register
; + const imgsize = 100; + const basedata = ( +
+ +
+ +
+ + {loginViewLoading ? ( +
+ + Waiting for the Shuffle database to become available. This may + take up to a minute. + + + {loginInfo === undefined || + loginInfo === null || + loginInfo.length === 0 ? null : ( +
Database Response: {loginInfo}
+ )} + + + + + + Are you sure Shuffle is{" "} + + installed correctly + + ? + + + + 1. Make sure shuffle-database folder has correct access, and that you have a minimum of 2Gb of RAM available:{" "} +
+
+ sudo chown -R 1000:1000 shuffle-database +
+ + 2. Disable memory swap on the host: +
+
+ sudo swapoff -a +
+ + 3. Restart the database: +
+
+ sudo docker restart shuffle-opensearch +
+
+ + Need help?{" "} + + Join the Discord! + + +
+ ) : ( +
+

{formtitle}

+ Username +
+ +
+ Password +
+ +
+ {MFAField === true ? ( +
+ 2-factor code + { + setMFAValue(event.target.value); + }} + /> +
+ ) : null} +
+ +
+
{loginInfo}
+ {ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? ( +
+ Or +
+ +
+
+ ) : null} +
+ )} +
+
+ ); + + const loadedCheck = isLoaded ?
{basedata}
:
; + + useEffect(() => { + setTimeout(() => { + if (ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0) { + //id="sso_button" + const ssoBtn = document.getElementById("sso_button"); + if (ssoBtn !== undefined && ssoBtn !== null) { + //console.log("SSO BTN: ", ssoBtn) + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + var tmpView = new URLSearchParams(cursearch).get("autologin"); + if (tmpView !== undefined && tmpView !== null) { + if (tmpView === "true") { + console.log("Tmp: ", tmpView) + ssoBtn.click() + } + } + } + } + }, 200); + }, [ssoUrl]) + + return
{loadedCheck}
; +}; + +export default LoginDialog; From 4a95979373ed91b0156533030a0ad4f37a53392e Mon Sep 17 00:00:00 2001 From: monilprajapati Date: Tue, 4 Mar 2025 00:13:31 +0530 Subject: [PATCH 038/200] Added New navbar --- .../public/images/ProfessionalServices.svg | 6 + .../public/images/SecurityConsultation.svg | 5 + frontend/public/images/Support.svg | 3 + frontend/public/images/Training.svg | 6 + frontend/public/images/icons/API.svg | 3 + frontend/public/images/icons/about_us.svg | 6 + .../public/images/icons/about_us_hover.svg | 6 + frontend/public/images/icons/articles.svg | 7 + .../public/images/icons/articles_hover.svg | 7 + frontend/public/images/icons/contact_us.svg | 3 + .../public/images/icons/contact_us_hover.svg | 3 + frontend/public/images/icons/discord.svg | 10 + frontend/public/images/icons/docs.svg | 4 + frontend/public/images/icons/docs_hover.svg | 4 + frontend/public/images/icons/faq.svg | 5 + frontend/public/images/icons/faq_hover.svg | 5 + frontend/public/images/icons/github.svg | 11 + frontend/public/images/icons/linkedIn.svg | 10 + frontend/public/images/icons/usecases.svg | 6 + .../public/images/icons/usecases_hover.svg | 6 + frontend/public/images/icons/x.svg | 10 + frontend/public/images/logos/singul.svg | 22 + frontend/src/App.jsx | 16 +- frontend/src/views/LoginPage.jsx | 4 +- frontend/src/views/Navbar.jsx | 2580 +++++++++++++++++ 25 files changed, 2745 insertions(+), 3 deletions(-) create mode 100644 frontend/public/images/ProfessionalServices.svg create mode 100644 frontend/public/images/SecurityConsultation.svg create mode 100644 frontend/public/images/Support.svg create mode 100644 frontend/public/images/Training.svg create mode 100644 frontend/public/images/icons/API.svg create mode 100644 frontend/public/images/icons/about_us.svg create mode 100644 frontend/public/images/icons/about_us_hover.svg create mode 100644 frontend/public/images/icons/articles.svg create mode 100644 frontend/public/images/icons/articles_hover.svg create mode 100644 frontend/public/images/icons/contact_us.svg create mode 100644 frontend/public/images/icons/contact_us_hover.svg create mode 100644 frontend/public/images/icons/discord.svg create mode 100644 frontend/public/images/icons/docs.svg create mode 100644 frontend/public/images/icons/docs_hover.svg create mode 100644 frontend/public/images/icons/faq.svg create mode 100644 frontend/public/images/icons/faq_hover.svg create mode 100644 frontend/public/images/icons/github.svg create mode 100644 frontend/public/images/icons/linkedIn.svg create mode 100644 frontend/public/images/icons/usecases.svg create mode 100644 frontend/public/images/icons/usecases_hover.svg create mode 100644 frontend/public/images/icons/x.svg create mode 100644 frontend/public/images/logos/singul.svg create mode 100644 frontend/src/views/Navbar.jsx diff --git a/frontend/public/images/ProfessionalServices.svg b/frontend/public/images/ProfessionalServices.svg new file mode 100644 index 00000000..164b6917 --- /dev/null +++ b/frontend/public/images/ProfessionalServices.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/images/SecurityConsultation.svg b/frontend/public/images/SecurityConsultation.svg new file mode 100644 index 00000000..05614a4d --- /dev/null +++ b/frontend/public/images/SecurityConsultation.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/public/images/Support.svg b/frontend/public/images/Support.svg new file mode 100644 index 00000000..83a18f00 --- /dev/null +++ b/frontend/public/images/Support.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/images/Training.svg b/frontend/public/images/Training.svg new file mode 100644 index 00000000..b01a9901 --- /dev/null +++ b/frontend/public/images/Training.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/images/icons/API.svg b/frontend/public/images/icons/API.svg new file mode 100644 index 00000000..447b94de --- /dev/null +++ b/frontend/public/images/icons/API.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/images/icons/about_us.svg b/frontend/public/images/icons/about_us.svg new file mode 100644 index 00000000..c77e7e8e --- /dev/null +++ b/frontend/public/images/icons/about_us.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/images/icons/about_us_hover.svg b/frontend/public/images/icons/about_us_hover.svg new file mode 100644 index 00000000..2c693ca9 --- /dev/null +++ b/frontend/public/images/icons/about_us_hover.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/images/icons/articles.svg b/frontend/public/images/icons/articles.svg new file mode 100644 index 00000000..763d960f --- /dev/null +++ b/frontend/public/images/icons/articles.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/public/images/icons/articles_hover.svg b/frontend/public/images/icons/articles_hover.svg new file mode 100644 index 00000000..6b51f294 --- /dev/null +++ b/frontend/public/images/icons/articles_hover.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/public/images/icons/contact_us.svg b/frontend/public/images/icons/contact_us.svg new file mode 100644 index 00000000..5fbc3568 --- /dev/null +++ b/frontend/public/images/icons/contact_us.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/images/icons/contact_us_hover.svg b/frontend/public/images/icons/contact_us_hover.svg new file mode 100644 index 00000000..0da02b7c --- /dev/null +++ b/frontend/public/images/icons/contact_us_hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/images/icons/discord.svg b/frontend/public/images/icons/discord.svg new file mode 100644 index 00000000..b982260b --- /dev/null +++ b/frontend/public/images/icons/discord.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/public/images/icons/docs.svg b/frontend/public/images/icons/docs.svg new file mode 100644 index 00000000..a568538d --- /dev/null +++ b/frontend/public/images/icons/docs.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/public/images/icons/docs_hover.svg b/frontend/public/images/icons/docs_hover.svg new file mode 100644 index 00000000..a711ba59 --- /dev/null +++ b/frontend/public/images/icons/docs_hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/public/images/icons/faq.svg b/frontend/public/images/icons/faq.svg new file mode 100644 index 00000000..7345040f --- /dev/null +++ b/frontend/public/images/icons/faq.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/public/images/icons/faq_hover.svg b/frontend/public/images/icons/faq_hover.svg new file mode 100644 index 00000000..4d29215a --- /dev/null +++ b/frontend/public/images/icons/faq_hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/public/images/icons/github.svg b/frontend/public/images/icons/github.svg new file mode 100644 index 00000000..de6c0200 --- /dev/null +++ b/frontend/public/images/icons/github.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/public/images/icons/linkedIn.svg b/frontend/public/images/icons/linkedIn.svg new file mode 100644 index 00000000..605e7804 --- /dev/null +++ b/frontend/public/images/icons/linkedIn.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/public/images/icons/usecases.svg b/frontend/public/images/icons/usecases.svg new file mode 100644 index 00000000..cc96e086 --- /dev/null +++ b/frontend/public/images/icons/usecases.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/images/icons/usecases_hover.svg b/frontend/public/images/icons/usecases_hover.svg new file mode 100644 index 00000000..6841b21e --- /dev/null +++ b/frontend/public/images/icons/usecases_hover.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/images/icons/x.svg b/frontend/public/images/icons/x.svg new file mode 100644 index 00000000..815e4e30 --- /dev/null +++ b/frontend/public/images/icons/x.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/public/images/logos/singul.svg b/frontend/public/images/logos/singul.svg new file mode 100644 index 00000000..5f27d7a3 --- /dev/null +++ b/frontend/public/images/logos/singul.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9fc34790..cd8d49d5 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -62,6 +62,7 @@ import Drift from "react-driftjs"; import { AppContext } from './context/ContextApi.jsx'; import Workflows2 from "./views/Workflows2.jsx"; import AppExplorer from "./views/AppExplorer.jsx"; +import Navbar from "./views/Navbar.jsx"; // Production - backend proxy forwarding in nginx var globalUrl = window.location.origin; @@ -213,7 +214,20 @@ const App = (message, props) => { { window?.location?.pathname === "/" || window?.location?.pathname === "/training" || !(isLoggedIn && isLoaded) ? (
-
*/} + {
{ + const theme = useTheme(); + return ( + + + + + + + ); +}; + +// Add this new component for mobile menu +const MobileMenu = ({ anchorEl, handleClose, isLoggedIn, navigate, isCloud }) => { + const [openSection, setOpenSection] = useState(null); + const theme = useTheme(); + + // Add useEffect to handle body scroll + useEffect(() => { + if (Boolean(anchorEl)) { + // Disable scroll + document.body.style.overflow = 'hidden'; + } else { + // Re-enable scroll + document.body.style.overflow = 'auto'; + } + + // Cleanup function to ensure scroll is re-enabled when component unmounts + return () => { + document.body.style.overflow = 'auto'; + }; + }, [anchorEl]); + + const handleSectionClick = (section) => { + setOpenSection(openSection === section ? null : section); + }; + + const handleItemClick = (path) => { + handleClose(); + navigate(path); + }; + + const showDesktopToast = () => { + toast.info("Please open on desktop for the full experience"); + handleClose(); + }; + + return ( + + + {Object.keys(menuData).map((section) => ( + + + + + {section === 'Resources' ? ( + + {menuData.Resources.columns.map((column, index) => ( + + + {column.title} + + + {column.social ? ( + <> + + {column.platforms.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + window.open(platform.link, '_blank') + return; + }} + // onClick={() => showDesktopToast()} + > + {platform.name} + + ))} + + + Follow Us + + + {column.followUs.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + window.open(platform.link, '_blank') + return; + }} + > + {platform.name} + + ))} + + + ) : ( + column.items.map((item) => ( + + )) + )} + + ))} + + ) : ( + + {menuData[section].map((item) => ( + + ))} + + )} + + + ))} + + {/* Static menu items */} + + + + {/* Action buttons */} + + + + + + + ); +}; + +const Navbar = (props) => { + const { + globalUrl, + isLoaded, + isLoggedIn, + removeCookie, + homePage, + userdata, + // isMobile, + serverside, + billingInfo, + + notifications, + } = props; + const theme = useTheme(); + const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); + const [pricingModalOpen, setPricingModalOpen] = useState(false); + const isTabletOrMobile = useMediaQuery(theme.breakpoints.down("lg")); + const isMobile = useMediaQuery(theme.breakpoints.down("md")); + const [anchorElNav, setAnchorElNav] = useState(null); + const [openMenu, setOpenMenu] = useState(null); + const [selectedOrganization, setSelectedOrganization] = useState({}); + const navigate = useNavigate(); + const [anchorElUser, setAnchorElUser] = useState(null); + const [anchorElOrg, setAnchorElOrg] = useState(null); + const [isOrgChanging, setIsOrgChanging] = useState(false); + const [isHovered, setIsHovered] = useState(""); + const [orgSelectOpen, setOrgSelectOpen] = useState(false); + const [showTopbar, setShowTopbar] = useState(true) // Set to true to show top bar + const isCloud = + serverside === true || typeof window === "undefined" + ? true + : window.location.host === "localhost:3002" || + window.location.host === "shuffler.io" || + window.location.host === "localhost:5002"; + + + const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_51PXYYMEJjT17t98N20qEqItyt1fLQjrnn41lPeG2PjnSlZHTDNKHuisAbW00s4KAn86nGuqB9uSVU4ds8MutbnMU00DPXpZ8ZD" : "pk_test_51PXYYMEJjT17t98NbDkojZ3DRvsFUQBs35LGMx3i436BXwEBVFKB9nCvHt0Q3M4MG3dz4mHheuWvfoYvpaL3GmsG00k1Rb2ksO" + + const topbar_var = "topbar_closed5" + useEffect(() => { + // Manually setShowTopbar(true) to show topbar by default + const topbar = localStorage.getItem(topbar_var) + if (topbar === "true") { + setShowTopbar(false) + } + }, []) + + const pricingModal = + { + setPricingModalOpen(false); + }} + PaperProps={{ + style: { + color: "white", + minWidth: 850, + minHeight: 370, + padding: 20, + backgroundColor: "rgba(0, 0, 0, 1)", + borderRadius: theme.palette?.borderRadius, + }, + }} + > + + + Upgrade your plan + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "close_upgrade_modal", + label: "navbar_upgrade_modal", + }) + }; + setPricingModalOpen(false); + }} + style={{ + marginLeft: "auto", + position: "absolute", + top: 20, + right: 20, + }} + > + + + +
+ +
+
+ + + const modalView = ( + { + setSearchBarModalOpen(false); + }} + PaperProps={{ + style: { + color: "white", + minWidth: 750, + height: 785, + borderRadius: 16, + border: "1px solid var(--Container-Stroke, #494949)", + background: "var(--Container, #000000)", + boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)", + }, + }} + sx={{ + zIndex: 50005, + '& .MuiBackdrop-root': { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + }, + }} + > + + + Search for Docs, Apps, Workflows and more + + setSearchBarModalOpen(false)} + sx={{ + color: 'white', + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)' + } + }} + > + + + + + + + + + + + ); + + const handleOpenNavMenu = (event) => { + setAnchorElNav(event.currentTarget); + }; + + const handleCloseNavMenu = () => { + setAnchorElNav(null); + }; + + const handleMenuOpen = (menu) => { + setOpenMenu(menu); + }; + + const handleMenuClose = () => { + setOpenMenu(null); + }; + + const handleOpenUserMenu = (event) => { + setAnchorElUser(event.currentTarget); + }; + + const handleCloseUserMenu = () => { + setAnchorElUser(null); + }; + + const handleOpenOrgMenu = (event) => { + setAnchorElOrg(event.currentTarget); + }; + + const handleCloseOrgMenu = () => { + setAnchorElOrg(null); + }; + + const menuStyles = { + "& .MuiPaper-root": { + backgroundColor: "#212121", + marginTop: 1, + backdropFilter: "blur(10px)", + fontFamily: theme.typography.fontFamily, + }, + }; + + const menuItemBox = { + display: "flex", + alignItems: "flex-start", + padding: 2, + paddingTop: "20px", + paddingBottom: "20px", + paddingLeft: "20px", + gap: 2, + width: "100%", + backgroundColor: "#212121", + transition: "all 0.2s ease-in-out", + borderBottom: "1px solid #494949", + }; + + const buttonStyles = { + fontFamily: theme.typography.fontFamily, + color: "white", + textTransform: "none", + fontSize: "16px", + fontWeight: 400, + padding: "6px 12px", + "& .MuiSvgIcon-root": { + transition: "transform 0.2s ease-in-out", + }, + "&:hover": { + backgroundColor: "transparent", + color: theme.palette.primary.main, + "& .MuiSvgIcon-root": { + transform: "rotate(180deg)", + }, + }, + }; + + // Render menu content based on type + const renderMenuContent = (item, isCloud) => { + if (item === "Resources") { + return ( + + {menuData.Resources.columns.map((column, index) => ( + + + {column.title} + + + {column.social ? ( + <> + + {column.platforms.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + handleMenuClose(); + }} + sx={{ + padding: 0, + width: 48, + height: 48, + "&:hover": { + backgroundColor: "transparent", + opacity: 0.8, + }, + }} + > + {platform.name} + + ))} + + + Follow Us + + + {column.followUs.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + handleMenuClose(); + }} + sx={{ + padding: 0, + width: 48, + height: 48, + "&:hover": { + backgroundColor: "transparent", + opacity: 0.8, + }, + }} + > + {platform.name} + + ))} + + + ) : ( + + {column.items.map((item) => ( + + ))} + + )} + + ))} + + ); + } + + return menuData[item].map((menuItem, index) => ( + { + if (menuItem.title === "Singul") { + window.open(menuItem.path, '_blank'); + return; + } + if(isCloud) { + ReactGA.event(menuItem.gaData); + } + handleCloseNavMenu(); + handleMenuClose(); + if(isCloud) { + navigate(menuItem.path); + } else { + if(menuItem.path.includes("docs")){ + navigate(menuItem.path); + handleMenuClose(); + }else{ + window.open("https://shuffler.io" + menuItem.path, '_blank'); + return; + } + } + }} + sx={{ + padding: 0, + backgroundColor: "#212121", + "&:hover": { + backgroundColor: "#212121", + }, + "&:last-child": { + borderBottomLeftRadius: "4px", + borderBottomRightRadius: "4px", + }, + "&:first-of-type": { + paddingTop: "4px", + }, + }} + > + + + + + + {menuItem.title} + + {menuItem.title === "Singul" && ( + + Coming Soon + + )} + + + {menuItem.description} + + + + + )); + }; + + const renderDesktopMenu = () => ( + + {Object.keys(menuData).map((item) => ( + + + + {/* Custom Dropdown */} + handleMenuOpen(item)} + onMouseLeave={handleMenuClose} + sx={{ + position: "absolute", + top: "130%", + left: + item === "Products" + ? 0 + : item === "Services" + ? -50 + : item === "Resources" + ? isTabletOrMobile ? -330 : -250 + : 0, + backgroundColor: "#212121", + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + borderBottomLeftRadius: "4px", + borderBottomRightRadius: "4px", + boxShadow: + "0 8px 16px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.1)", + backdropFilter: "blur(10px)", + zIndex: -20, + opacity: openMenu === item ? 1 : 0, + visibility: openMenu === item ? "visible" : "hidden", + transform: openMenu === item ? "translateY(0)" : "translateY(-8px)", + transition: "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease", + pointerEvents: openMenu === item ? "auto" : "none", + ...(item === "Resources" && { + width: "900px", + padding: 3, + }), + }} + > + {renderMenuContent(item, isCloud)} + + + ))} + + + + ); + + const menuItemStyles = { + fontFamily: theme.typography.fontFamily, + color: "white", + "&:hover": { + backgroundColor: "rgba(255, 133, 68, 0.1)", + }, + }; + + const searchButtonStyles = { + fontFamily: theme.typography.fontFamily, + color: "rgba(255, 255, 255, 0.7)", + border: "1px solid rgba(26, 26, 26, 0.8)", + backgroundColor: "#2F2F2F", + textTransform: "none", + borderRadius: "8px", + fontSize: "14px", + padding: "6px 12px", + display: "flex", + alignItems: "center", + height: "42px", + gap: 1, + minWidth: "110px", + justifyContent: "space-between", + "&:hover": { + borderColor: "rgba(255, 255, 255, 0.1)", + }, + }; + + const searchIconButtonStyles = { + color: "rgba(255, 255, 255, 0.7)", + padding: "8px", + minWidth: "unset", + height: "42px", + "&:hover": { + backgroundColor: "#3F3F3F", + }, + }; + + // Add this shared button style + const sharedButtonStyles = { + fontFamily: theme.typography.fontFamily, + textTransform: "none", + fontSize: "16px", + fontWeight: 600, + padding: "6px 24px", + }; + + + useEffect(() => { + Mousetrap.bind(['command+k', 'ctrl+k'], () => { + setSearchBarModalOpen(true); + return false; // Prevent the default action + }); + Mousetrap.bind(['esc'], () => { + setSearchBarModalOpen(false); + return false; // Prevent the default action + }); + + return () => { + Mousetrap.unbind(['command+k', 'ctrl+k']); + }; + }, []); + + useEffect(() => { + if (isLoggedIn && userdata?.active_org?.id?.length > 0) { + handleGetOrg(userdata.active_org.id); + } + }, [isLoggedIn]); + + const handleGetOrg = (orgId) => { + fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.status === 401) { + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson["success"] === false) { + console.log("Error getting org: ", responseJson); + } else { + setSelectedOrganization(responseJson); + } + }) + .catch((error) => { + console.log("Error getting org: ", error); + }); + }; + + const handleClickLogout = () => { + console.log("SHOULD LOG OUT"); + + toast.info("Logging out..."); + // 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 + removeCookie("session_token", { path: "/" }); + removeCookie("session_token", { path: "/" }); + removeCookie("session_token", { path: "/" }); + + removeCookie("__session", { path: "/" }); + removeCookie("__session", { path: "/" }); + removeCookie("__session", { path: "/" }); + + removeCookie("__session", { path: "/" }); + + window.location.pathname = "/"; + + localStorage.setItem("globalUrl", "") + + // Delete userinfo from localstorage + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") + }) + .catch((error) => { + console.log(error); + }); + }; + + const topbarHeight = showTopbar ? 40 : 0 + const topbar = !isCloud || !showTopbar ? null : + curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/usecases2" || curpath === "/training" || curpath === "/professional-services" ? + +
+ + {/* Shuffle 1.4.0 is out! Read more about  */} + New  + + { + ReactGA.event({ + category: "landingpage", + action: "click_header_training", + label: "", + }) + + navigate("/training") + + }} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.9)" }}> + Public Training + + +  Dates Released! + + { + setShowTopbar(false) + + // Set storage that it's clicked + localStorage.setItem(topbar_var, "true") + }}> + + +
+
+ : + null + + const handleClickChangeOrg = (orgId) => { + setIsOrgChanging(true); + const data = { org_id: orgId }; + + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); + toast.info("Changing organization..."); + fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify(data), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } else { + localStorage.removeItem("apps"); + localStorage.removeItem("workflows"); + localStorage.removeItem("userinfo"); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + if ( + responseJson.region_url !== undefined && + responseJson.region_url !== null && + responseJson.region_url.length > 0 + ) { + console.log("Region Change: ", responseJson.region_url); + localStorage.setItem("globalUrl", responseJson.region_url); + //globalUrl = responseJson.region_url + } + + if (responseJson["reason"] === "SSO_REDIRECT") { + toast.info("Redirecting to SSO login page as SSO is required for this organization."); + setTimeout(() => { + window.location.href = responseJson["url"]; + }, 2000); + } else { + toast("Successfully changed active organization - refreshing!"); + setTimeout(() => { + window.location.reload(); + }, 2000); + } + } else { + setIsOrgChanging(false); + if ( + responseJson.reason !== undefined && + responseJson.reason !== null && + responseJson.reason.length > 0 + ) { + toast(responseJson.reason); + } else { + toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); + } + } + }) + .catch((error) => { + console.log("error changing: ", error); + setIsOrgChanging(false); + }); + }; + + // Add these helper functions from LeftSideBar + const getRegionTag = (region_url) => { + let regiontag = "EU"; + if (region_url !== undefined && region_url !== null && region_url.length > 0) { + const regionsplit = region_url.split("."); + if (regionsplit.length > 2 && !regionsplit[0].includes("shuffler")) { + const namesplit = regionsplit[0].split("/"); + regiontag = namesplit[namesplit.length - 1]; + + if (regiontag === "california") { + regiontag = "US"; + } else if (regiontag === "frankfurt") { + regiontag = "EU-2"; + } else if (regiontag === "ca") { + regiontag = "CA"; + } + } + } + return regiontag; + }; + + const getRegionFlag = (region_url) => { + const regionTag = getRegionTag(region_url); + const regionMapping = { + "US": "us", + "EU": "eu", + "EU-2": "de", + "CA": "ca", + "UK": "gb" + }; + + const region = regionMapping[regionTag] || "eu"; + return `https://flagcdn.com/48x36/${region}.png`; + }; + + // Add this useEffect to prevent scroll locking + useEffect(() => { + // Remove modal classes that cause scroll locking + const removeModalClasses = () => { + document.body.style.overflow = 'auto'; + document.body.style.paddingRight = '0px'; + }; + + // Create observer to watch for class changes + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.attributeName === 'class') { + if (document.body.classList.contains('MuiModal-open')) { + removeModalClasses(); + } + } + }); + }); + + // Start observing + observer.observe(document.body, { + attributes: true, + attributeFilter: ['class'], + }); + + // Cleanup + return () => { + observer.disconnect(); + removeModalClasses(); + }; + }, []); + + const handleOrgSelectClick = (event) => { + // Prevent the default Select behavior + event.preventDefault(); + if(isCloud){ + ReactGA.event({ + category: "navbar", + action: "org_dropdown_click", + label: "org_dropdown_click", + }) + } + // Toggle the select open state + setOrgSelectOpen(!orgSelectOpen); + }; + + return ( + + {topbar} + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "home_click", + label: "shuffle_logo", + }); + } + }} + > + shuffle logo + + + + {isMobile ? ( + /* Mobile Navigation Icons */ + + setSearchBarModalOpen(true)} + > + + + { + if(Boolean(anchorElNav)) { + handleCloseNavMenu(); + }else{ + handleOpenNavMenu(e); + } + } + } + sx={{ color: "white" }} + > + {Boolean(anchorElNav) ? ( + + ) : ( + + )} + + + + ) : ( + <> + {modalView} + {pricingModal} + {renderDesktopMenu()} + {/* Right Side Buttons */} + + {!isLoaded ? ( + + ) : isLoggedIn ? ( + <> + {isOrgChanging ? ( + + ) : ( + <> + + + + K +
+ } + placement="bottom" + arrow + componentsProps={{ + tooltip: { + sx: { + backgroundColor: "rgba(33, 33, 33, 1)", + color: "rgba(241, 241, 241, 1)", + fontSize: 12, + border: "1px solid rgba(73, 73, 73, 1)", + fontFamily: theme?.typography?.fontFamily, + } + }, + popper: { + sx: { + zIndex: 1000019, + } + } + }} + > + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "search_icon_click", + label: "search_icon_click", + }) + } + setSearchBarModalOpen(true); + }} + > + + + + {isCloud && (userdata.org_status === undefined || userdata.org_status === null || userdata.org_status.length === 0) ? + + : null} + + + {/* Organization Dropdown */} + {/* */} + + {/* User Avatar Menu */} + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "click_user_menu", + label: "open_user_dropdown" + }); + } + handleOpenUserMenu(e); + }} + sx={{ + padding: 0, + "&:hover": { + backgroundColor: "rgba(47, 47, 47, 0.5)", + }, + }} + > + + + + + + + {/* User Info Section */} + + + + + + {userdata?.username} + + + + + + + + {/* Menu Items */} + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_admin" + }); + } + handleCloseUserMenu(); + navigate("/admin"); + }} + onMouseEnter={() => setIsHovered("organization")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "organization" ? "#FF8544" : "white", + }, + }} + > + + + + Organization + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_settings", + }) + } + handleCloseUserMenu(); + navigate("/settings"); + }} + onMouseEnter={() => setIsHovered("settings")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "settings" ? "#FF8544" : "white", + }, + }} + > + + + + Settings + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_notifications", + }) + } + handleCloseUserMenu(); + navigate("/admin?admin_tab=notifications"); + }} + onMouseEnter={() => setIsHovered("notifications")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "notifications" ? "#FF8544" : "white", + }, + }} + > + + + + Notifications ({notifications === undefined || notifications === null ? 0 : + notifications?.filter((notification) => notification.read === false).length}) + + + + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_about", + }) + } + handleCloseUserMenu(); + navigate("/docs/about"); + }} + onMouseEnter={() => setIsHovered("about")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "about" ? "#FF8544" : "white", + }, + }} + > + + + + About + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "logout_click", + }) + } + handleCloseUserMenu(); + handleClickLogout(); + }} + onMouseEnter={() => setIsHovered("logout")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "logout" ? "#FD4C62" : "white", + }, + }} + > + + + + Logout + + + + + + + + + Version 1.4.5 + + + + + )} + + ) : ( + <> + + + + K +
+ } + placement="bottom" + arrow + componentsProps={{ + tooltip: { + sx: { + backgroundColor: "rgba(33, 33, 33, 1)", + color: "rgba(241, 241, 241, 1)", + fontSize: 12, + border: "1px solid rgba(73, 73, 73, 1)", + fontFamily: theme?.typography?.fontFamily, + } + }, + popper: { + sx: { + zIndex: 1000019, + } + } + }} + > + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "search_icon_click", + label: "search_icon_click", + }) + } + setSearchBarModalOpen(true); + }} + > + + + + + + + + + )} + + + )} + + + + ); +}; + +export default Navbar; From ad07379508d1abe7d0ac02baa5a1205e6d9585a1 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 19:52:13 +0100 Subject: [PATCH 039/200] Minor fixes EVERYWHERE --- backend/go-app/go.mod | 2 +- frontend/src/App.jsx | 42 +++++++++++++- frontend/src/components/LeftSideBar.jsx | 41 ++++++++----- frontend/src/components/TenantsTab.jsx | 2 +- frontend/src/views/AdminSetup.jsx | 28 +++++---- frontend/src/views/AngularWorkflow.jsx | 53 +++++++++-------- frontend/src/views/LoginPage.jsx | 76 +++++++++++++++++++------ frontend/src/views/LoginPageOld.jsx | 12 ++-- 8 files changed, 184 insertions(+), 72 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 37174871..477c6f4a 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle go 1.22.2 -//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared require ( cloud.google.com/go/datastore v1.15.0 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9fc34790..a24b4daf 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -34,7 +34,7 @@ import RunWorkflow from "./views/RunWorkflow.jsx"; import Admin2 from "./views/Admin2.jsx"; import LoginPage from "./views/LoginPage.jsx"; -//import LoginPage from "./views/LoginPage.jsx"; +import LoginPageOld from "./views/LoginPageOld.jsx"; import SettingsPage from "./views/SettingsPage.jsx"; import KeepAlive from "./views/KeepAlive.jsx"; @@ -257,6 +257,44 @@ const App = (message, props) => { } /> + + } + /> + + + } + /> + { } /> ) : null} + { /> } /> + { return orgOptions.find((option) => option.name === selectedOrg); }, [selectedOrg, orgOptions]); -//With this code it is opening search bar on google chrome search bar as well which is not required -useEffect(() => { - const handleKeyDown = (event) => { - if ((event.ctrlKey || event.metaKey) && event.key === "k") { - event.preventDefault(); - setSearchBarModalOpen((prev)=> !prev); - } - }; + //With this code it is opening search bar on google chrome search bar as well which is not required + useEffect(() => { + const handleKeyDown = (event) => { + if ((event.ctrlKey || event.metaKey) && event.key === "k") { + event.preventDefault(); + setSearchBarModalOpen((prev)=> !prev); + } + }; - window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keydown", handleKeyDown); - return () => { - window.removeEventListener("keydown", handleKeyDown); - }; -}, [setSearchBarModalOpen]); + return () => { + window.removeEventListener("keydown", handleKeyDown); + }; + }, [setSearchBarModalOpen]); const CustomPopper = (props) => { return ( @@ -155,6 +155,7 @@ useEffect(() => { > {props.children} + { marginLeft: 5, }} > + + {expandLeftNav && + + } { }; export default LeftSideBar; - - const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata}) => { return ( ( diff --git a/frontend/src/components/TenantsTab.jsx b/frontend/src/components/TenantsTab.jsx index afeb9563..907c982e 100644 --- a/frontend/src/components/TenantsTab.jsx +++ b/frontend/src/components/TenantsTab.jsx @@ -785,7 +785,7 @@ const TenantsTab = memo((props) => {

Tenants

- Control sub organizations (tenants)! {" "} + Create, manage and change to sub-organizations (tenants)! {" "} {isCloud ? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out." : ''} diff --git a/frontend/src/views/AdminSetup.jsx b/frontend/src/views/AdminSetup.jsx index f8f3d747..0365cf2f 100755 --- a/frontend/src/views/AdminSetup.jsx +++ b/frontend/src/views/AdminSetup.jsx @@ -1,6 +1,8 @@ /* eslint-disable react/no-multi-comp */ import React, { useState } from "react"; import { makeStyles } from "@mui/styles"; +import theme from '../theme.jsx'; +import { useNavigate } from "react-router-dom"; import { CircularProgress, @@ -20,11 +22,8 @@ const surfaceColor = "#27292D"; const inputColor = "#383B40"; const boxStyle = { - paddingLeft: "30px", - paddingRight: "30px", - paddingBottom: "30px", - paddingTop: "30px", - backgroundColor: surfaceColor, + padding: 40, + backgroundColor: theme.palette.backgroundColor, }; const useStyles = makeStyles({ @@ -41,8 +40,10 @@ const AdminAccount = (props) => { const [firstRequest, setFirstRequest] = useState(true); const [loginLoading, setLoginLoading] = useState(false); + // Used to swap from login to register. True = login, false = register const register = true; + let navigate = useNavigate(); const classes = useStyles(); // Error messages etc @@ -70,13 +71,16 @@ const AdminAccount = (props) => { setLoginInfo(responseJson["reason"]); } else { if (responseJson.reason === "redirect") { - window.location.pathname = "/login"; + setTimeout(() => { + window.location.pathname = "/login"; + }, 2500) } } }) ) .catch((error) => { - setLoginInfo("Error in userdata: ", error); + setLoginInfo("Error in userdata (1): ", error); + navigate("/loginsetup") }); }; @@ -108,13 +112,17 @@ const AdminAccount = (props) => { setLoginInfo(responseJson["reason"]); } else { setLoginInfo("Successful register :)"); - window.location.pathname = "/login"; + + setTimeout(() => { + window.location.pathname = "/login"; + }, 2500) } }) ) .catch((error) => { - setLoginLoading(false); - setLoginInfo("Error in userdata: ", error); + setLoginInfo("Error in userdata (2): ", error); + setLoginLoading(false) + navigate("/loginsetup") }); }; diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 51812543..5f9eadd2 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -2540,18 +2540,21 @@ const AngularWorkflow = (defaultprops) => { } */ + // FIXME: What is this? if (cy !== undefined && cy !== null) { // scale: 0.3, // bg: "#27292d", - const cyImageData = cy.png({ - output: "base64uri", - maxWidth: 480, - maxHeight: 270, - }) + if (cy.png !== undefined && cy.png !== null) { + const cyImageData = cy.png({ + output: "base64uri", + maxWidth: 480, + maxHeight: 270, + }) - if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { - useworkflow.image = cyImageData - } + if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { + useworkflow.image = cyImageData + } + } } if (useworkflow.id === undefined || useworkflow.id === null || useworkflow.id.length === 0) { @@ -9785,9 +9788,14 @@ const AngularWorkflow = (defaultprops) => { toast("Successfully stopped schedule"); } - workflow.triggers[triggerindex].status = "stopped"; - trigger.status = "stopped"; - setSelectedTrigger(trigger); + if (triggerindex !== undefined && triggerindex !== null && triggerindex >= 0) { + workflow.triggers[triggerindex].status = "stopped"; + } + + //trigger.status = "stopped"; + //console.log("TRIGGER: ", trigger) + //setSelectedTrigger(trigger); + setWorkflow(workflow); saveWorkflow(workflow) @@ -14637,20 +14645,19 @@ const AngularWorkflow = (defaultprops) => { cy.add(cybranch); } - console.log("Value to be set: ", e.target.value); try { - workflow.triggers[ - selectedTriggerIndex - ].parameters[3].value = e.target.value.id; - } catch { - workflow.triggers[selectedTriggerIndex].parameters[3] = - { - name: "startnode", - value: e.target.value.id, - }; + workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id + } catch(e) { + console.log("Error: ", e) + workflow.triggers[selectedTriggerIndex].parameters[3] = + { + name: "startnode", + value: e.target.value.id, + }; } - setWorkflow(workflow); + setWorkflow(workflow) + setUpdate(Math.random()) } } @@ -15035,7 +15042,7 @@ const AngularWorkflow = (defaultprops) => { {data.image !== undefined && data.image !== null && data.image.length > 0 ? - {data.name} + {data.name} : null} Choose Subflow '{data.name}' diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index ece6897c..030781ab 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -1,9 +1,10 @@ /* eslint-disable react/no-multi-comp */ -import React, { useState } from 'react'; +import React, { useState, useEffect, } from 'react'; import { makeStyles } from '@mui/styles'; import { useNavigate, Link, useParams } from "react-router-dom"; import { isMobile } from "react-device-detect"; import { toast } from 'react-toastify'; +import { useInterval } from "react-powerhooks"; import { @@ -259,7 +260,7 @@ const MarketplaceCard = ({ classes }) => { target="_blank" style={{ color: "rgba(255, 132, 68, 1)", textDecoration: "underline" }} > - Read more about self hosting + Learn more about self hosting
@@ -269,7 +270,7 @@ const MarketplaceCard = ({ classes }) => { const LoginPage = props => { - const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, inregister, serverside } = props; + const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, inregister, serverside, checkLogin, } = props; let navigate = useNavigate(); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); @@ -284,6 +285,21 @@ const LoginPage = props => { const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io"; const parsedsearch = serverside === true ? "" : window.location.search + + useEffect(() => { + if (!isCloud) { + checkAdmin() + } + }, []) + + const { start, stop } = useInterval({ + duration: 3000, + startImmediate: false, + callback: () => { + checkAdmin() + }, + }) + if (serverside !== true) { const tmpMessage = new URLSearchParams(window.location.search).get("message") if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) { @@ -409,6 +425,38 @@ const LoginPage = props => { window.location.pathname = "/workflows" } + const checkAdmin = () => { + const url = globalUrl + "/api/v1/checkusers"; + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === false) { + setLoginInfo(responseJson["reason"]); + } else { + + // Stay = 0 users + // Redirect = >1 user + if (responseJson.reason === "stay") { + setTimeout(() => { + navigate("/adminsetup") + }, 2500) + } + } + }) + ) + .catch((error) => { + setTimeout(() => { + navigate("/adminsetup") + }, 2500) + }) + }; + + const onSubmit = (e) => { //toast("Testing from login page") @@ -629,12 +677,13 @@ const LoginPage = props => { width: "max-content", }} > - + Shuffle Logo { variant="body2" > {register - ? "Find new ways to automate by discovering usecases made by Shufflers" + ? "Find new ways to automate by discovering usecases Shufflers" : "Please fill in the information to continue to discover the power of Shuffle" } -
+
{isCloud ? "Email" : "Username"}
{
{!loginWithSSO && ( -
+
Password
{ flexDirection: "column", marginTop: "0px" }}> - {register && !loginWithSSO && ( + {isCloud && register && !loginWithSSO && ( { - if (!isCloud) { - toast.warn("Talk to your administrator") - } - }} style={{ ...hrefStyle, alignSelf: isMobile ? "center" : "flex-end" diff --git a/frontend/src/views/LoginPageOld.jsx b/frontend/src/views/LoginPageOld.jsx index aaf376cd..6b34b957 100755 --- a/frontend/src/views/LoginPageOld.jsx +++ b/frontend/src/views/LoginPageOld.jsx @@ -58,9 +58,9 @@ const LoginDialog = (props) => { // Used to swap from login to register. True = login, false = register - useEffect(() => { - checkAdmin() - }, [loginViewLoading]) + useEffect(() => { + checkAdmin() + }, [loginViewLoading]) // Error messages etc const [loginInfo, setLoginInfo] = useState(""); @@ -91,6 +91,8 @@ const LoginDialog = (props) => { if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { setSSOUrl(responseJson.sso_url); } + + navigate("/login") if (loginViewLoading) { setLoginViewLoading(false); @@ -125,7 +127,7 @@ const LoginDialog = (props) => { callback: () => { checkAdmin(); }, - }); + }) if (firstRequest) { setFirstRequest(false); @@ -294,7 +296,7 @@ const LoginDialog = (props) => { style={{ marginBottom: 20, color: "white" }} > Waiting for the Shuffle database to become available. This may - take up to a minute. + take up to two minutes. {loginInfo === undefined || From 7eaca42a45a5c018297828ddc9098917a2a1a27e Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 21:09:15 +0100 Subject: [PATCH 040/200] Minor loginpage / navbar fixes --- frontend/src/views/LoginPage.jsx | 2 +- frontend/src/views/Navbar.jsx | 71 +++++++++++++++++--------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 172c01aa..1c3c3f80 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -256,7 +256,7 @@ const MarketplaceCard = ({ classes }) => { */} diff --git a/frontend/src/views/Navbar.jsx b/frontend/src/views/Navbar.jsx index 933e63a4..9cee6f74 100644 --- a/frontend/src/views/Navbar.jsx +++ b/frontend/src/views/Navbar.jsx @@ -203,7 +203,7 @@ const menuData = { social: true, platforms: [ { name: "Discord", icon: "/images/icons/discord.svg", link: "https://discord.gg/B2CBzUm", gaData: { category: "navbar", action: "social_click", label: "discord_icon_click" } }, - { name: "GitHub", icon: "/images/icons/github.svg", link: "https://github.com/shuffle/shuffle", gaData: { category: "navbar", action: "social_click", label: "github_icon_click" } }, + { name: "GitHub", icon: "/images/icons/github.svg", link: "https://github.com/shuffle/shuffle/blob/main/.github/install-guide.md", gaData: { category: "navbar", action: "social_click", label: "github_icon_click" } }, ], followUs: [ { name: "LinkedIn", icon: "/images/icons/linkedIn.svg", link: "https://www.linkedin.com/company/shuffleio", gaData: { category: "navbar", action: "social_click", label: "linkedin_icon_click" } }, @@ -403,7 +403,7 @@ const MobileMenu = ({ anchorEl, handleClose, isLoggedIn, navigate, isCloud }) => fontSize: '14px', fontFamily: theme.typography.fontFamily, }}> - {column.title} + {column.title} {column.social ? ( @@ -1103,9 +1103,9 @@ const Navbar = (props) => { if (isCloud) { ReactGA.event(item.gaData); handleMenuClose(); - } else if(item.link.includes("docs") || item.link.includes("usecases")){ + } else if (item.link.includes("docs") || item.link.includes("usecases")) { handleMenuClose(); - }else{ + } else { window.open("https://shuffler.io" + item.link, '_blank'); return; } @@ -2509,36 +2509,39 @@ const Navbar = (props) => { > Login - + + {isCloud && + + } - - - {section === 'Resources' ? ( - - {menuData.Resources.columns.map((column, index) => ( - - - {column.title} - - - {column.social ? ( - <> - - {column.platforms.map((platform) => ( - { - if (isCloud) { - ReactGA.event(platform.gaData); - } - window.open(platform.link, '_blank') - return; - }} - // onClick={() => showDesktopToast()} - > - {platform.name} - - ))} - - - Follow Us - - - {column.followUs.map((platform) => ( - { - if (isCloud) { - ReactGA.event(platform.gaData); - } - window.open(platform.link, '_blank') - return; - }} - > - {platform.name} - - ))} - - - ) : ( - column.items.map((item) => ( - - )) - )} - - ))} - - ) : ( - - {menuData[section].map((item) => ( - - ))} - - )} - - - ))} - - {/* Static menu items */} - - - - {/* Action buttons */} - - - - - - - ); -}; - -const Navbar = (props) => { - const { - globalUrl, - isLoaded, - isLoggedIn, - removeCookie, - homePage, - userdata, - // isMobile, - serverside, - billingInfo, - - notifications, - } = props; - const theme = useTheme(); - const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); - const [pricingModalOpen, setPricingModalOpen] = useState(false); - const isTabletOrMobile = useMediaQuery(theme.breakpoints.down("lg")); - const isMobile = useMediaQuery(theme.breakpoints.down("md")); - const [anchorElNav, setAnchorElNav] = useState(null); - const [openMenu, setOpenMenu] = useState(null); - const [selectedOrganization, setSelectedOrganization] = useState({}); - const navigate = useNavigate(); - const [anchorElUser, setAnchorElUser] = useState(null); - const [anchorElOrg, setAnchorElOrg] = useState(null); - const [isOrgChanging, setIsOrgChanging] = useState(false); - const [isHovered, setIsHovered] = useState(""); - const [orgSelectOpen, setOrgSelectOpen] = useState(false); - const [showTopbar, setShowTopbar] = useState(true) // Set to true to show top bar - const isCloud = - serverside === true || typeof window === "undefined" - ? true - : window.location.host === "localhost:3002" || - window.location.host === "shuffler.io" || - window.location.host === "localhost:5002"; - - - const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_51PXYYMEJjT17t98N20qEqItyt1fLQjrnn41lPeG2PjnSlZHTDNKHuisAbW00s4KAn86nGuqB9uSVU4ds8MutbnMU00DPXpZ8ZD" : "pk_test_51PXYYMEJjT17t98NbDkojZ3DRvsFUQBs35LGMx3i436BXwEBVFKB9nCvHt0Q3M4MG3dz4mHheuWvfoYvpaL3GmsG00k1Rb2ksO" - - const topbar_var = "topbar_closed5" - useEffect(() => { - // Manually setShowTopbar(true) to show topbar by default - const topbar = localStorage.getItem(topbar_var) - if (topbar === "true") { - setShowTopbar(false) - } - }, []) - - const pricingModal = - { - setPricingModalOpen(false); - }} - PaperProps={{ - style: { - color: "white", - minWidth: 850, - minHeight: 370, - padding: 20, - backgroundColor: "rgba(0, 0, 0, 1)", - borderRadius: theme.palette?.borderRadius, - }, - }} - > - - - Upgrade your plan - - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "close_upgrade_modal", - label: "navbar_upgrade_modal", - }) - }; - setPricingModalOpen(false); - }} - style={{ - marginLeft: "auto", - position: "absolute", - top: 20, - right: 20, - }} - > - - - -
- -
-
- - - const modalView = ( - { - setSearchBarModalOpen(false); - }} - PaperProps={{ - style: { - color: "white", - minWidth: 750, - height: 785, - borderRadius: 16, - border: "1px solid var(--Container-Stroke, #494949)", - background: "var(--Container, #000000)", - boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)", - }, - }} - sx={{ - zIndex: 50005, - '& .MuiBackdrop-root': { - backgroundColor: 'rgba(0, 0, 0, 0.8)', - }, - }} - > - - - Search for Docs, Apps, Workflows and more - - setSearchBarModalOpen(false)} - sx={{ - color: 'white', - '&:hover': { - backgroundColor: 'rgba(255, 255, 255, 0.1)' - } - }} - > - - - - - - - - - - - ); - - const handleOpenNavMenu = (event) => { - setAnchorElNav(event.currentTarget); - }; - - const handleCloseNavMenu = () => { - setAnchorElNav(null); - }; - - const handleMenuOpen = (menu) => { - setOpenMenu(menu); - }; - - const handleMenuClose = () => { - setOpenMenu(null); - }; - - const handleOpenUserMenu = (event) => { - setAnchorElUser(event.currentTarget); - }; - - const handleCloseUserMenu = () => { - setAnchorElUser(null); - }; - - const handleOpenOrgMenu = (event) => { - setAnchorElOrg(event.currentTarget); - }; - - const handleCloseOrgMenu = () => { - setAnchorElOrg(null); - }; - - const menuStyles = { - "& .MuiPaper-root": { - backgroundColor: "#212121", - marginTop: 1, - backdropFilter: "blur(10px)", - fontFamily: theme.typography.fontFamily, - }, - }; - - const menuItemBox = { - display: "flex", - alignItems: "flex-start", - padding: 2, - paddingTop: "20px", - paddingBottom: "20px", - paddingLeft: "20px", - gap: 2, - width: "100%", - backgroundColor: "#212121", - transition: "all 0.2s ease-in-out", - borderBottom: "1px solid #494949", - }; - - const buttonStyles = { - fontFamily: theme.typography.fontFamily, - color: "white", - textTransform: "none", - fontSize: "16px", - fontWeight: 400, - padding: "6px 12px", - "& .MuiSvgIcon-root": { - transition: "transform 0.2s ease-in-out", - }, - "&:hover": { - backgroundColor: "transparent", - color: theme.palette.primary.main, - "& .MuiSvgIcon-root": { - transform: "rotate(180deg)", - }, - }, - }; - - // Render menu content based on type - const renderMenuContent = (item, isCloud) => { - if (item === "Resources") { - return ( - - {menuData.Resources.columns.map((column, index) => ( - - - {column.title} - - - {column.social ? ( - <> - - {column.platforms.map((platform) => ( - { - if (isCloud) { - ReactGA.event(platform.gaData); - } - handleMenuClose(); - }} - sx={{ - padding: 0, - width: 48, - height: 48, - "&:hover": { - backgroundColor: "transparent", - opacity: 0.8, - }, - }} - > - {platform.name} - - ))} - - - Follow Us - - - {column.followUs.map((platform) => ( - { - if (isCloud) { - ReactGA.event(platform.gaData); - } - handleMenuClose(); - }} - sx={{ - padding: 0, - width: 48, - height: 48, - "&:hover": { - backgroundColor: "transparent", - opacity: 0.8, - }, - }} - > - {platform.name} - - ))} - - - ) : ( - - {column.items.map((item) => ( - - ))} - - )} - - ))} - - ); - } - - return menuData[item].map((menuItem, index) => ( - { - if (menuItem.title === "Singul") { - window.open(menuItem.path, '_blank'); - return; - } - if(isCloud) { - ReactGA.event(menuItem.gaData); - } - handleCloseNavMenu(); - handleMenuClose(); - if(isCloud) { - navigate(menuItem.path); - } else { - if(menuItem.path.includes("docs")){ - navigate(menuItem.path); - handleMenuClose(); - }else{ - window.open("https://shuffler.io" + menuItem.path, '_blank'); - return; - } - } - }} - sx={{ - padding: 0, - backgroundColor: "#212121", - "&:hover": { - backgroundColor: "#212121", - }, - "&:last-child": { - borderBottomLeftRadius: "4px", - borderBottomRightRadius: "4px", - }, - "&:first-of-type": { - paddingTop: "4px", - }, - }} - > - - - - - - {menuItem.title} - - {menuItem.title === "Singul" && ( - - Coming Soon - - )} - - - {menuItem.description} - - - - - )); - }; - - const renderDesktopMenu = () => ( - - {Object.keys(menuData).map((item) => ( - - - - {/* Custom Dropdown */} - handleMenuOpen(item)} - onMouseLeave={handleMenuClose} - sx={{ - position: "absolute", - top: "130%", - left: - item === "Products" - ? 0 - : item === "Services" - ? -50 - : item === "Resources" - ? isTabletOrMobile ? -330 : -250 - : 0, - backgroundColor: "#212121", - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - borderBottomLeftRadius: "4px", - borderBottomRightRadius: "4px", - boxShadow: - "0 8px 16px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.1)", - backdropFilter: "blur(10px)", - zIndex: -20, - opacity: openMenu === item ? 1 : 0, - visibility: openMenu === item ? "visible" : "hidden", - transform: openMenu === item ? "translateY(0)" : "translateY(-8px)", - transition: "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease", - pointerEvents: openMenu === item ? "auto" : "none", - ...(item === "Resources" && { - width: "900px", - padding: 3, - }), - }} - > - {renderMenuContent(item, isCloud)} - - - ))} - - - - ); - - const menuItemStyles = { - fontFamily: theme.typography.fontFamily, - color: "white", - "&:hover": { - backgroundColor: "rgba(255, 133, 68, 0.1)", - }, - }; - - const searchButtonStyles = { - fontFamily: theme.typography.fontFamily, - color: "rgba(255, 255, 255, 0.7)", - border: "1px solid rgba(26, 26, 26, 0.8)", - backgroundColor: "#2F2F2F", - textTransform: "none", - borderRadius: "8px", - fontSize: "14px", - padding: "6px 12px", - display: "flex", - alignItems: "center", - height: "42px", - gap: 1, - minWidth: "110px", - justifyContent: "space-between", - "&:hover": { - borderColor: "rgba(255, 255, 255, 0.1)", - }, - }; - - const searchIconButtonStyles = { - color: "rgba(255, 255, 255, 0.7)", - padding: "8px", - minWidth: "unset", - height: "42px", - "&:hover": { - backgroundColor: "#3F3F3F", - }, - }; - - // Add this shared button style - const sharedButtonStyles = { - fontFamily: theme.typography.fontFamily, - textTransform: "none", - fontSize: "16px", - fontWeight: 600, - padding: "6px 24px", - }; - - - useEffect(() => { - Mousetrap.bind(['command+k', 'ctrl+k'], () => { - setSearchBarModalOpen(true); - return false; // Prevent the default action - }); - Mousetrap.bind(['esc'], () => { - setSearchBarModalOpen(false); - return false; // Prevent the default action - }); - - return () => { - Mousetrap.unbind(['command+k', 'ctrl+k']); - }; - }, []); - - useEffect(() => { - if (isLoggedIn && userdata?.active_org?.id?.length > 0) { - handleGetOrg(userdata.active_org.id); - } - }, [isLoggedIn]); - - const handleGetOrg = (orgId) => { - fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => { - if (response.status === 401) { - } - - return response.json(); - }) - .then((responseJson) => { - if (responseJson["success"] === false) { - console.log("Error getting org: ", responseJson); - } else { - setSelectedOrganization(responseJson); - } - }) - .catch((error) => { - console.log("Error getting org: ", error); - }); - }; - - const handleClickLogout = () => { - console.log("SHOULD LOG OUT"); - - toast.info("Logging out..."); - // 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 - removeCookie("session_token", { path: "/" }); - removeCookie("session_token", { path: "/" }); - removeCookie("session_token", { path: "/" }); - - removeCookie("__session", { path: "/" }); - removeCookie("__session", { path: "/" }); - removeCookie("__session", { path: "/" }); - - removeCookie("__session", { path: "/" }); - - window.location.pathname = "/"; - - localStorage.setItem("globalUrl", "") - - // Delete userinfo from localstorage - localStorage.removeItem("apps") - localStorage.removeItem("workflows") - localStorage.removeItem("userinfo") - }) - .catch((error) => { - console.log(error); - }); - }; - - const topbarHeight = showTopbar ? 40 : 0 - const topbar = !isCloud || !showTopbar ? null : - curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/usecases2" || curpath === "/training" || curpath === "/professional-services" ? - -
- - {/* Shuffle 1.4.0 is out! Read more about  */} - New  - - { - ReactGA.event({ - category: "landingpage", - action: "click_header_training", - label: "", - }) - - navigate("/training") - - }} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.9)" }}> - Public Training - - -  Dates Released! - - { - setShowTopbar(false) - - // Set storage that it's clicked - localStorage.setItem(topbar_var, "true") - }}> - - -
-
- : - null - - const handleClickChangeOrg = (orgId) => { - setIsOrgChanging(true); - const data = { org_id: orgId }; - - localStorage.setItem("globalUrl", ""); - localStorage.setItem("getting_started_sidebar", "open"); - toast.info("Changing organization..."); - fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { - mode: "cors", - credentials: "include", - crossDomain: true, - method: "POST", - body: JSON.stringify(data), - withCredentials: true, - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - }) - .then(function (response) { - if (response.status !== 200) { - console.log("Error in response"); - } else { - localStorage.removeItem("apps"); - localStorage.removeItem("workflows"); - localStorage.removeItem("userinfo"); - } - - return response.json(); - }) - .then(function (responseJson) { - if (responseJson.success === true) { - if ( - responseJson.region_url !== undefined && - responseJson.region_url !== null && - responseJson.region_url.length > 0 - ) { - console.log("Region Change: ", responseJson.region_url); - localStorage.setItem("globalUrl", responseJson.region_url); - //globalUrl = responseJson.region_url - } - - if (responseJson["reason"] === "SSO_REDIRECT") { - toast.info("Redirecting to SSO login page as SSO is required for this organization."); - setTimeout(() => { - window.location.href = responseJson["url"]; - }, 2000); - } else { - toast("Successfully changed active organization - refreshing!"); - setTimeout(() => { - window.location.reload(); - }, 2000); - } - } else { - setIsOrgChanging(false); - if ( - responseJson.reason !== undefined && - responseJson.reason !== null && - responseJson.reason.length > 0 - ) { - toast(responseJson.reason); - } else { - toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); - } - } - }) - .catch((error) => { - console.log("error changing: ", error); - setIsOrgChanging(false); - }); - }; - - // Add these helper functions from LeftSideBar - const getRegionTag = (region_url) => { - let regiontag = "EU"; - if (region_url !== undefined && region_url !== null && region_url.length > 0) { - const regionsplit = region_url.split("."); - if (regionsplit.length > 2 && !regionsplit[0].includes("shuffler")) { - const namesplit = regionsplit[0].split("/"); - regiontag = namesplit[namesplit.length - 1]; - - if (regiontag === "california") { - regiontag = "US"; - } else if (regiontag === "frankfurt") { - regiontag = "EU-2"; - } else if (regiontag === "ca") { - regiontag = "CA"; - } - } - } - return regiontag; - }; - - const getRegionFlag = (region_url) => { - const regionTag = getRegionTag(region_url); - const regionMapping = { - "US": "us", - "EU": "eu", - "EU-2": "de", - "CA": "ca", - "UK": "gb" - }; - - const region = regionMapping[regionTag] || "eu"; - return `https://flagcdn.com/48x36/${region}.png`; - }; - - // Add this useEffect to prevent scroll locking - useEffect(() => { - // Remove modal classes that cause scroll locking - const removeModalClasses = () => { - document.body.style.overflow = 'auto'; - document.body.style.paddingRight = '0px'; - }; - - // Create observer to watch for class changes - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.attributeName === 'class') { - if (document.body.classList.contains('MuiModal-open')) { - removeModalClasses(); - } - } - }); - }); - - // Start observing - observer.observe(document.body, { - attributes: true, - attributeFilter: ['class'], - }); - - // Cleanup - return () => { - observer.disconnect(); - removeModalClasses(); - }; - }, []); - - const handleOrgSelectClick = (event) => { - // Prevent the default Select behavior - event.preventDefault(); - if(isCloud){ - ReactGA.event({ - category: "navbar", - action: "org_dropdown_click", - label: "org_dropdown_click", - }) - } - // Toggle the select open state - setOrgSelectOpen(!orgSelectOpen); - }; - - return ( - - {topbar} - - - - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "home_click", - label: "shuffle_logo", - }); - } - }} - > - shuffle logo - - - - {isMobile ? ( - /* Mobile Navigation Icons */ - - setSearchBarModalOpen(true)} - > - - - { - if(Boolean(anchorElNav)) { - handleCloseNavMenu(); - }else{ - handleOpenNavMenu(e); - } - } - } - sx={{ color: "white" }} - > - {Boolean(anchorElNav) ? ( - - ) : ( - - )} - - - - ) : ( - <> - {modalView} - {pricingModal} - {renderDesktopMenu()} - {/* Right Side Buttons */} - - {!isLoaded ? ( - - ) : isLoggedIn ? ( - <> - {isOrgChanging ? ( - - ) : ( - <> - - - + K -
- } - placement="bottom" - arrow - componentsProps={{ - tooltip: { - sx: { - backgroundColor: "rgba(33, 33, 33, 1)", - color: "rgba(241, 241, 241, 1)", - fontSize: 12, - border: "1px solid rgba(73, 73, 73, 1)", - fontFamily: theme?.typography?.fontFamily, - } - }, - popper: { - sx: { - zIndex: 1000019, - } - } - }} - > - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "search_icon_click", - label: "search_icon_click", - }) - } - setSearchBarModalOpen(true); - }} - > - - - - {isCloud && (userdata.org_status === undefined || userdata.org_status === null || userdata.org_status.length === 0) ? - - : null} - - - {/* Organization Dropdown */} - {/* */} - - {/* User Avatar Menu */} - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "click_user_menu", - label: "open_user_dropdown" - }); - } - handleOpenUserMenu(e); - }} - sx={{ - padding: 0, - "&:hover": { - backgroundColor: "rgba(47, 47, 47, 0.5)", - }, - }} - > - - - - - - - {/* User Info Section */} - - - - - - {userdata?.username} - - - - - - - - {/* Menu Items */} - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "user_dropdown_click", - label: "go_to_admin" - }); - } - handleCloseUserMenu(); - navigate("/admin"); - }} - onMouseEnter={() => setIsHovered("organization")} - onMouseLeave={() => setIsHovered("")} - sx={{ - py: 1.5, - px: 2, - fontFamily: theme.typography.fontFamily, - transition: "color 0.1s ease", - '&:hover': { - backgroundColor: 'rgba(255, 255, 255, 0.1)', - color: isHovered === "organization" ? "#FF8544" : "white", - }, - }} - > - - - - Organization - - - - - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "user_dropdown_click", - label: "go_to_settings", - }) - } - handleCloseUserMenu(); - navigate("/settings"); - }} - onMouseEnter={() => setIsHovered("settings")} - onMouseLeave={() => setIsHovered("")} - sx={{ - py: 1.5, - px: 2, - fontFamily: theme.typography.fontFamily, - transition: "color 0.1s ease", - '&:hover': { - backgroundColor: 'rgba(255, 255, 255, 0.1)', - color: isHovered === "settings" ? "#FF8544" : "white", - }, - }} - > - - - - Settings - - - - - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "user_dropdown_click", - label: "go_to_notifications", - }) - } - handleCloseUserMenu(); - navigate("/admin?admin_tab=notifications"); - }} - onMouseEnter={() => setIsHovered("notifications")} - onMouseLeave={() => setIsHovered("")} - sx={{ - py: 1.5, - px: 2, - fontFamily: theme.typography.fontFamily, - transition: "color 0.1s ease", - '&:hover': { - backgroundColor: 'rgba(255, 255, 255, 0.1)', - color: isHovered === "notifications" ? "#FF8544" : "white", - }, - }} - > - - - - Notifications ({notifications === undefined || notifications === null ? 0 : - notifications?.filter((notification) => notification.read === false).length}) - - - - - - - - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "user_dropdown_click", - label: "go_to_about", - }) - } - handleCloseUserMenu(); - navigate("/docs/about"); - }} - onMouseEnter={() => setIsHovered("about")} - onMouseLeave={() => setIsHovered("")} - sx={{ - py: 1.5, - px: 2, - fontFamily: theme.typography.fontFamily, - transition: "color 0.1s ease", - '&:hover': { - backgroundColor: 'rgba(255, 255, 255, 0.1)', - color: isHovered === "about" ? "#FF8544" : "white", - }, - }} - > - - - - About - - - - - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "user_dropdown_click", - label: "logout_click", - }) - } - handleCloseUserMenu(); - handleClickLogout(); - }} - onMouseEnter={() => setIsHovered("logout")} - onMouseLeave={() => setIsHovered("")} - sx={{ - py: 1.5, - px: 2, - fontFamily: theme.typography.fontFamily, - '&:hover': { - backgroundColor: 'rgba(255, 255, 255, 0.1)', - color: isHovered === "logout" ? "#FD4C62" : "white", - }, - }} - > - - - - Logout - - - - - - - - - Version 1.4.5 - - - - - )} - - ) : ( - <> - - - + K -
- } - placement="bottom" - arrow - componentsProps={{ - tooltip: { - sx: { - backgroundColor: "rgba(33, 33, 33, 1)", - color: "rgba(241, 241, 241, 1)", - fontSize: 12, - border: "1px solid rgba(73, 73, 73, 1)", - fontFamily: theme?.typography?.fontFamily, - } - }, - popper: { - sx: { - zIndex: 1000019, - } - } - }} - > - { - if (isCloud) { - ReactGA.event({ - category: "navbar", - action: "search_icon_click", - label: "search_icon_click", - }) - } - setSearchBarModalOpen(true); - }} - > - - - - - - - {isCloud && - - } - - - )} - - - )} - - - - ); -}; - -export default Navbar; From ea41d8c79dbb8234733b6f6d219a808931204f8a Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 21:38:57 +0100 Subject: [PATCH 043/200] Re-added navbar --- frontend/src/components/Navbar.jsx | 2585 ++++++++++++++++++++++++++++ 1 file changed, 2585 insertions(+) create mode 100644 frontend/src/components/Navbar.jsx diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx new file mode 100644 index 00000000..509b9b4f --- /dev/null +++ b/frontend/src/components/Navbar.jsx @@ -0,0 +1,2585 @@ +import React, { useEffect, useState } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import { + AppBar, + Box, + Toolbar, + IconButton, + Typography, + Menu, + MenuItem, + Button, + Container, + useTheme, + useMediaQuery, + Divider, + alpha, + Avatar, + Select, + Tooltip, + DialogTitle, + DialogContent, + Dialog, + Slide, + Collapse +} from "@mui/material"; +import MenuIcon from "@mui/icons-material/Menu"; +import { Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material' +import SearchBox from "../components/SearchData.jsx"; +import ReactGA from "react-ga4"; +import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import KeyboardCommandKeyIcon from '@mui/icons-material/KeyboardCommandKey'; +import SearchIcon from "@mui/icons-material/Search"; +// import SearchField from "../components/Searchfield.jsx"; +import { toast } from "react-toastify"; +import AddIcon from '@mui/icons-material/Add'; +import Mousetrap from "mousetrap"; +import LicencePopup from "../components/LicencePopup.jsx"; + +const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string") +? window.location.pathname +: ""; + +// Menu Data Structure +const menuData = { + Products: [ + { + title: "Shuffle", + description: + "The most versatile automation engine with focus on security.", + icon: "images/logos/orange_logo.svg", + path: "/docs", + gaData: { + category: "navbar", + action: "products_click", + label: "shuffle_logo_click" + } + }, + { + title: "Singul", + description: + "Connect and run actions seamlessly between different platforms.", + icon: "/images/logos/singul.svg", + path: "https://singul.io/", + gaData: { + category: "navbar", + action: "products_click", + label: "singul_click" + } + }, + { + title: "API Explorer", + description: + "Explore, run and automate APIs from over 2500 platforms.", + icon: "/images/icons/API.svg", + path: "/apis", + gaData: { + category: "navbar", + action: "products_click", + label: "api_explorer_click" + } + }, + ], + Services: [ + { + title: "Professional Services", + description: + "Professional Services help you solve problems at your convenience.", + icon: "/images/ProfessionalServices.svg", + path: "/professional-services", + gaData: { + category: "navbar", + action: "services_click", + label: "professional_services_click" + } + }, + { + title: "Support", + description: + "Support to help you build automations with confidence.", + icon: "/images/Support.svg", + path: "/contact?category=support", + gaData: { + category: "navbar", + action: "services_click", + label: "support_click" + } + }, + { + title: "Training", + description: + "Tailored to provide hands-on learning of Shuffle for automation mastery.", + icon: "/images/Training.svg", + path: "/training", + gaData: { + category: "navbar", + action: "services_click", + label: "training_click" + } + }, + { + title: "Security Consultation", + description: + "Automate your infrastructure with expert guidance and tailored solutions.", + icon: "/images/SecurityConsultation.svg", + path: "/contact?category=security_consultation", + gaData: { + category: "navbar", + action: "services_click", + label: "security_consultation_click" + } + }, + ], + Resources: { + columns: [ + { + title: "Platform", + items: [ + { + title: "Usecases", + icon: "/images/icons/usecases.svg", + hoverIcon: "/images/icons/usecases_hover.svg", + link: "/usecases", + gaData: { + category: "navbar", + action: "resources_click", + label: "usecases_click" + } + }, + { + title: "Documentation", + icon: "/images/icons/docs.svg", + hoverIcon: "/images/icons/docs_hover.svg", + link: "/docs", + gaData: { + category: "navbar", + action: "resources_click", + label: "documentation_click" + } + }, + { title: "FAQ", icon: "/images/icons/faq.svg", hoverIcon: "/images/icons/faq_hover.svg", link: "/faq", gaData: { category: "navbar", action: "resources_click", label: "faq" } }, + ], + }, + { + title: "Company", + items: [ + { + title: "About us", + icon: "/images/icons/about_us.svg", + hoverIcon: "/images/icons/about_us_hover.svg", + link: "/docs/about", + gaData: { + category: "navbar", + action: "resources_click", + label: "about_us_click" + } + }, + { + title: "Articles", + icon: "/images/icons/articles.svg", + hoverIcon: "/images/icons/articles_hover.svg", + link: "/articles", + gaData: { + category: "navbar", + action: "resources_click", + label: "articles_click" + } + }, + { + title: "Contact Us", + icon: "/images/icons/contact_us.svg", + hoverIcon: "/images/icons/contact_us_hover.svg", + link: "/contact?category=contact", + gaData: { + category: "navbar", + action: "resources_click", + label: "contact_us_click" + } + }, + ], + }, + { + title: "Join the community", + social: true, + platforms: [ + { name: "Discord", icon: "/images/icons/discord.svg", link: "https://discord.gg/B2CBzUm", gaData: { category: "navbar", action: "social_click", label: "discord_icon_click" } }, + { name: "GitHub", icon: "/images/icons/github.svg", link: "https://github.com/shuffle/shuffle/blob/main/.github/install-guide.md", gaData: { category: "navbar", action: "social_click", label: "github_icon_click" } }, + ], + followUs: [ + { name: "LinkedIn", icon: "/images/icons/linkedIn.svg", link: "https://www.linkedin.com/company/shuffleio", gaData: { category: "navbar", action: "social_click", label: "linkedin_icon_click" } }, + { name: "Twitter", icon: "/images/icons/x.svg", link: "https://twitter.com/shuffleio", gaData: { category: "navbar", action: "social_click", label: "twitter_icon_click" } }, + ], + }, + ], + }, +}; + +// Add this new component at the top level of the file, right after the imports +const LoadingSkeleton = () => { + const theme = useTheme(); + return ( + + + + + + + ); +}; + +// Add this new component for mobile menu +const MobileMenu = ({ anchorEl, handleClose, isLoggedIn, navigate, isCloud }) => { + const [openSection, setOpenSection] = useState(null); + const theme = useTheme(); + + // Add useEffect to handle body scroll + useEffect(() => { + if (Boolean(anchorEl)) { + // Disable scroll + document.body.style.overflow = 'hidden'; + } else { + // Re-enable scroll + document.body.style.overflow = 'auto'; + } + + // Cleanup function to ensure scroll is re-enabled when component unmounts + return () => { + document.body.style.overflow = 'auto'; + }; + }, [anchorEl]); + + const handleSectionClick = (section) => { + setOpenSection(openSection === section ? null : section); + }; + + const handleItemClick = (path) => { + handleClose(); + navigate(path); + }; + + const showDesktopToast = () => { + toast.info("Please open on desktop for the full experience"); + handleClose(); + }; + + return ( + + + {Object.keys(menuData).map((section) => ( + + + + + {section === 'Resources' ? ( + + {menuData.Resources.columns.map((column, index) => ( + + + {column.title} + + + {column.social ? ( + <> + + {column.platforms.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + window.open(platform.link, '_blank') + return; + }} + // onClick={() => showDesktopToast()} + > + {platform.name} + + ))} + + + Follow Us + + + {column.followUs.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + window.open(platform.link, '_blank') + return; + }} + > + {platform.name} + + ))} + + + ) : ( + column.items.map((item) => ( + + )) + )} + + ))} + + ) : ( + + {menuData[section].map((item) => ( + + ))} + + )} + + + ))} + + {/* Static menu items */} + + + + {/* Action buttons */} + + + + + + + ); +}; + +const Navbar = (props) => { + const { + globalUrl, + isLoaded, + isLoggedIn, + removeCookie, + homePage, + userdata, + // isMobile, + serverside, + billingInfo, + + notifications, + } = props; + + const topbar_var = "topbar_closed10" + + const theme = useTheme(); + const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); + const [pricingModalOpen, setPricingModalOpen] = useState(false); + const isTabletOrMobile = useMediaQuery(theme.breakpoints.down("lg")); + const isMobile = useMediaQuery(theme.breakpoints.down("md")); + const [anchorElNav, setAnchorElNav] = useState(null); + const [openMenu, setOpenMenu] = useState(null); + const [selectedOrganization, setSelectedOrganization] = useState({}); + const navigate = useNavigate(); + const [anchorElUser, setAnchorElUser] = useState(null); + const [anchorElOrg, setAnchorElOrg] = useState(null); + const [isOrgChanging, setIsOrgChanging] = useState(false); + const [isHovered, setIsHovered] = useState(""); + const [orgSelectOpen, setOrgSelectOpen] = useState(false); + const [showTopbar, setShowTopbar] = useState(true) // Set to true to show top bar + const isCloud = + serverside === true || typeof window === "undefined" + ? true + : window.location.host === "localhost:3002" || + window.location.host === "shuffler.io" || + window.location.host === "localhost:5002"; + + + const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_51PXYYMEJjT17t98N20qEqItyt1fLQjrnn41lPeG2PjnSlZHTDNKHuisAbW00s4KAn86nGuqB9uSVU4ds8MutbnMU00DPXpZ8ZD" : "pk_test_51PXYYMEJjT17t98NbDkojZ3DRvsFUQBs35LGMx3i436BXwEBVFKB9nCvHt0Q3M4MG3dz4mHheuWvfoYvpaL3GmsG00k1Rb2ksO" + + useEffect(() => { + // Manually setShowTopbar(true) to show topbar by default + const topbar = localStorage.getItem(topbar_var) + if (topbar === "true") { + setShowTopbar(false) + } + }, []) + + const pricingModal = + { + setPricingModalOpen(false); + }} + PaperProps={{ + style: { + color: "white", + minWidth: 850, + minHeight: 370, + padding: 20, + backgroundColor: "rgba(0, 0, 0, 1)", + borderRadius: theme.palette?.borderRadius, + }, + }} + > + + + Upgrade your plan + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "close_upgrade_modal", + label: "navbar_upgrade_modal", + }) + }; + setPricingModalOpen(false); + }} + style={{ + marginLeft: "auto", + position: "absolute", + top: 20, + right: 20, + }} + > + + + +
+ +
+
+ + + const modalView = ( + { + setSearchBarModalOpen(false); + }} + PaperProps={{ + style: { + color: "white", + minWidth: 750, + height: 785, + borderRadius: 16, + border: "1px solid var(--Container-Stroke, #494949)", + background: "var(--Container, #000000)", + boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)", + }, + }} + sx={{ + zIndex: 50005, + '& .MuiBackdrop-root': { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + }, + }} + > + + + Search for Docs, Apps, Workflows and more + + setSearchBarModalOpen(false)} + sx={{ + color: 'white', + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)' + } + }} + > + + + + + + + + + + + ); + + const handleOpenNavMenu = (event) => { + setAnchorElNav(event.currentTarget); + }; + + const handleCloseNavMenu = () => { + setAnchorElNav(null); + }; + + const handleMenuOpen = (menu) => { + setOpenMenu(menu); + }; + + const handleMenuClose = () => { + setOpenMenu(null); + }; + + const handleOpenUserMenu = (event) => { + setAnchorElUser(event.currentTarget); + }; + + const handleCloseUserMenu = () => { + setAnchorElUser(null); + }; + + const handleOpenOrgMenu = (event) => { + setAnchorElOrg(event.currentTarget); + }; + + const handleCloseOrgMenu = () => { + setAnchorElOrg(null); + }; + + const menuStyles = { + "& .MuiPaper-root": { + backgroundColor: "#212121", + marginTop: 1, + backdropFilter: "blur(10px)", + fontFamily: theme.typography.fontFamily, + }, + }; + + const menuItemBox = { + display: "flex", + alignItems: "flex-start", + padding: 2, + paddingTop: "20px", + paddingBottom: "20px", + paddingLeft: "20px", + gap: 2, + width: "100%", + backgroundColor: "#212121", + transition: "all 0.2s ease-in-out", + borderBottom: "1px solid #494949", + }; + + const buttonStyles = { + fontFamily: theme.typography.fontFamily, + color: "white", + textTransform: "none", + fontSize: "16px", + fontWeight: 400, + padding: "6px 12px", + "& .MuiSvgIcon-root": { + transition: "transform 0.2s ease-in-out", + }, + "&:hover": { + backgroundColor: "transparent", + color: theme.palette.primary.main, + "& .MuiSvgIcon-root": { + transform: "rotate(180deg)", + }, + }, + }; + + // Render menu content based on type + const renderMenuContent = (item, isCloud) => { + if (item === "Resources") { + return ( + + {menuData.Resources.columns.map((column, index) => ( + + + {column.title} + + + {column.social ? ( + <> + + {column.platforms.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + handleMenuClose(); + }} + sx={{ + padding: 0, + width: 48, + height: 48, + "&:hover": { + backgroundColor: "transparent", + opacity: 0.8, + }, + }} + > + {platform.name} + + ))} + + + Follow Us + + + {column.followUs.map((platform) => ( + { + if (isCloud) { + ReactGA.event(platform.gaData); + } + handleMenuClose(); + }} + sx={{ + padding: 0, + width: 48, + height: 48, + "&:hover": { + backgroundColor: "transparent", + opacity: 0.8, + }, + }} + > + {platform.name} + + ))} + + + ) : ( + + {column.items.map((item) => ( + + ))} + + )} + + ))} + + ); + } + + return menuData[item].map((menuItem, index) => ( + { + if (menuItem.title === "Singul") { + window.open(menuItem.path, '_blank'); + return; + } + if(isCloud) { + ReactGA.event(menuItem.gaData); + } + handleCloseNavMenu(); + handleMenuClose(); + if(isCloud) { + navigate(menuItem.path); + } else { + if(menuItem.path.includes("docs")){ + navigate(menuItem.path); + handleMenuClose(); + }else{ + window.open("https://shuffler.io" + menuItem.path, '_blank'); + return; + } + } + }} + sx={{ + padding: 0, + backgroundColor: "#212121", + "&:hover": { + backgroundColor: "#212121", + }, + "&:last-child": { + borderBottomLeftRadius: "4px", + borderBottomRightRadius: "4px", + }, + "&:first-of-type": { + paddingTop: "4px", + }, + }} + > + + + + + + {menuItem.title} + + {menuItem.title === "Singul" && ( + + Coming Soon + + )} + + + {menuItem.description} + + + + + )); + }; + + const renderDesktopMenu = () => ( + + {Object.keys(menuData).map((item) => ( + + + + {/* Custom Dropdown */} + handleMenuOpen(item)} + onMouseLeave={handleMenuClose} + sx={{ + position: "absolute", + top: "130%", + left: + item === "Products" + ? 0 + : item === "Services" + ? -50 + : item === "Resources" + ? isTabletOrMobile ? -330 : -250 + : 0, + backgroundColor: "#212121", + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + borderBottomLeftRadius: "4px", + borderBottomRightRadius: "4px", + boxShadow: + "0 8px 16px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.1)", + backdropFilter: "blur(10px)", + zIndex: -20, + opacity: openMenu === item ? 1 : 0, + visibility: openMenu === item ? "visible" : "hidden", + transform: openMenu === item ? "translateY(0)" : "translateY(-8px)", + transition: "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease", + pointerEvents: openMenu === item ? "auto" : "none", + ...(item === "Resources" && { + width: "900px", + padding: 3, + }), + }} + > + {renderMenuContent(item, isCloud)} + + + ))} + + + + ); + + const menuItemStyles = { + fontFamily: theme.typography.fontFamily, + color: "white", + "&:hover": { + backgroundColor: "rgba(255, 133, 68, 0.1)", + }, + }; + + const searchButtonStyles = { + fontFamily: theme.typography.fontFamily, + color: "rgba(255, 255, 255, 0.7)", + border: "1px solid rgba(26, 26, 26, 0.8)", + backgroundColor: "#2F2F2F", + textTransform: "none", + borderRadius: "8px", + fontSize: "14px", + padding: "6px 12px", + display: "flex", + alignItems: "center", + height: "42px", + gap: 1, + minWidth: "110px", + justifyContent: "space-between", + "&:hover": { + borderColor: "rgba(255, 255, 255, 0.1)", + }, + }; + + const searchIconButtonStyles = { + color: "rgba(255, 255, 255, 0.7)", + padding: "8px", + minWidth: "unset", + height: "42px", + "&:hover": { + backgroundColor: "#3F3F3F", + }, + }; + + // Add this shared button style + const sharedButtonStyles = { + fontFamily: theme.typography.fontFamily, + textTransform: "none", + fontSize: "16px", + fontWeight: 600, + padding: "6px 24px", + }; + + + useEffect(() => { + Mousetrap.bind(['command+k', 'ctrl+k'], () => { + setSearchBarModalOpen(true); + return false; // Prevent the default action + }); + Mousetrap.bind(['esc'], () => { + setSearchBarModalOpen(false); + return false; // Prevent the default action + }); + + return () => { + Mousetrap.unbind(['command+k', 'ctrl+k']); + }; + }, []); + + useEffect(() => { + if (isLoggedIn && userdata?.active_org?.id?.length > 0) { + handleGetOrg(userdata.active_org.id); + } + }, [isLoggedIn]); + + const handleGetOrg = (orgId) => { + fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.status === 401) { + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson["success"] === false) { + console.log("Error getting org: ", responseJson); + } else { + setSelectedOrganization(responseJson); + } + }) + .catch((error) => { + console.log("Error getting org: ", error); + }); + }; + + const handleClickLogout = () => { + console.log("SHOULD LOG OUT"); + + toast.info("Logging out..."); + // 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 + removeCookie("session_token", { path: "/" }); + removeCookie("session_token", { path: "/" }); + removeCookie("session_token", { path: "/" }); + + removeCookie("__session", { path: "/" }); + removeCookie("__session", { path: "/" }); + removeCookie("__session", { path: "/" }); + + removeCookie("__session", { path: "/" }); + + window.location.pathname = "/"; + + localStorage.setItem("globalUrl", "") + + // Delete userinfo from localstorage + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") + }) + .catch((error) => { + console.log(error); + }); + }; + + const topbarHeight = showTopbar ? 40 : 0 + const topbar = !isCloud || !showTopbar ? null : + curpath === "/" || curpath.includes("/docs") || curpath === "/pricing" || curpath === "/contact" || curpath === "/search" || curpath === "/usecases" || curpath === "/training" || curpath === "/professional-services" ? + +
+ + {/* Shuffle 1.4.0 is out! Read more about  */} + Shuffle 2.0.0 is out now! + + { + ReactGA.event({ + category: "landingpage", + action: "click_header_training", + label: "", + }) + + navigate("/articles/2.0_release") + + }} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.9)" }}> +  Read about it here. + + + + { + setShowTopbar(false) + + // Set storage that it's clicked + localStorage.setItem(topbar_var, "true") + }}> + + +
+
+ : + null + + const handleClickChangeOrg = (orgId) => { + setIsOrgChanging(true); + const data = { org_id: orgId }; + + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); + toast.info("Changing organization..."); + fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify(data), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } else { + localStorage.removeItem("apps"); + localStorage.removeItem("workflows"); + localStorage.removeItem("userinfo"); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + if ( + responseJson.region_url !== undefined && + responseJson.region_url !== null && + responseJson.region_url.length > 0 + ) { + console.log("Region Change: ", responseJson.region_url); + localStorage.setItem("globalUrl", responseJson.region_url); + //globalUrl = responseJson.region_url + } + + if (responseJson["reason"] === "SSO_REDIRECT") { + toast.info("Redirecting to SSO login page as SSO is required for this organization."); + setTimeout(() => { + window.location.href = responseJson["url"]; + }, 2000); + } else { + toast("Successfully changed active organization - refreshing!"); + setTimeout(() => { + window.location.reload(); + }, 2000); + } + } else { + setIsOrgChanging(false); + if ( + responseJson.reason !== undefined && + responseJson.reason !== null && + responseJson.reason.length > 0 + ) { + toast(responseJson.reason); + } else { + toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); + } + } + }) + .catch((error) => { + console.log("error changing: ", error); + setIsOrgChanging(false); + }); + }; + + // Add these helper functions from LeftSideBar + const getRegionTag = (region_url) => { + let regiontag = "EU"; + if (region_url !== undefined && region_url !== null && region_url.length > 0) { + const regionsplit = region_url.split("."); + if (regionsplit.length > 2 && !regionsplit[0].includes("shuffler")) { + const namesplit = regionsplit[0].split("/"); + regiontag = namesplit[namesplit.length - 1]; + + if (regiontag === "california") { + regiontag = "US"; + } else if (regiontag === "frankfurt") { + regiontag = "EU-2"; + } else if (regiontag === "ca") { + regiontag = "CA"; + } + } + } + return regiontag; + }; + + const getRegionFlag = (region_url) => { + const regionTag = getRegionTag(region_url); + const regionMapping = { + "US": "us", + "EU": "eu", + "EU-2": "de", + "CA": "ca", + "UK": "gb" + }; + + const region = regionMapping[regionTag] || "eu"; + return `https://flagcdn.com/48x36/${region}.png`; + }; + + // Add this useEffect to prevent scroll locking + useEffect(() => { + // Remove modal classes that cause scroll locking + const removeModalClasses = () => { + document.body.style.overflow = 'auto'; + document.body.style.paddingRight = '0px'; + }; + + // Create observer to watch for class changes + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.attributeName === 'class') { + if (document.body.classList.contains('MuiModal-open')) { + removeModalClasses(); + } + } + }); + }); + + // Start observing + observer.observe(document.body, { + attributes: true, + attributeFilter: ['class'], + }); + + // Cleanup + return () => { + observer.disconnect(); + removeModalClasses(); + }; + }, []); + + const handleOrgSelectClick = (event) => { + // Prevent the default Select behavior + event.preventDefault(); + if(isCloud){ + ReactGA.event({ + category: "navbar", + action: "org_dropdown_click", + label: "org_dropdown_click", + }) + } + // Toggle the select open state + setOrgSelectOpen(!orgSelectOpen); + }; + + return ( + + {topbar} + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "home_click", + label: "shuffle_logo", + }); + } + }} + > + shuffle logo + + + + {isMobile ? ( + /* Mobile Navigation Icons */ + + setSearchBarModalOpen(true)} + > + + + { + if(Boolean(anchorElNav)) { + handleCloseNavMenu(); + }else{ + handleOpenNavMenu(e); + } + } + } + sx={{ color: "white" }} + > + {Boolean(anchorElNav) ? ( + + ) : ( + + )} + + + + ) : ( + <> + {modalView} + {pricingModal} + {renderDesktopMenu()} + {/* Right Side Buttons */} + + {!isLoaded ? ( + + ) : isLoggedIn ? ( + <> + {isOrgChanging ? ( + + ) : ( + <> + + + + K +
+ } + placement="bottom" + arrow + componentsProps={{ + tooltip: { + sx: { + backgroundColor: "rgba(33, 33, 33, 1)", + color: "rgba(241, 241, 241, 1)", + fontSize: 12, + border: "1px solid rgba(73, 73, 73, 1)", + fontFamily: theme?.typography?.fontFamily, + } + }, + popper: { + sx: { + zIndex: 1000019, + } + } + }} + > + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "search_icon_click", + label: "search_icon_click", + }) + } + setSearchBarModalOpen(true); + }} + > + + + + {isCloud && (userdata.org_status === undefined || userdata.org_status === null || userdata.org_status.length === 0) ? + + : null} + + + {/* Organization Dropdown */} + {/* */} + + {/* User Avatar Menu */} + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "click_user_menu", + label: "open_user_dropdown" + }); + } + handleOpenUserMenu(e); + }} + sx={{ + padding: 0, + "&:hover": { + backgroundColor: "rgba(47, 47, 47, 0.5)", + }, + }} + > + + + + + + + {/* User Info Section */} + + + + + + {userdata?.username} + + + + + + + + {/* Menu Items */} + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_admin" + }); + } + handleCloseUserMenu(); + navigate("/admin"); + }} + onMouseEnter={() => setIsHovered("organization")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "organization" ? "#FF8544" : "white", + }, + }} + > + + + + Organization + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_settings", + }) + } + handleCloseUserMenu(); + navigate("/settings"); + }} + onMouseEnter={() => setIsHovered("settings")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "settings" ? "#FF8544" : "white", + }, + }} + > + + + + Settings + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_notifications", + }) + } + handleCloseUserMenu(); + navigate("/admin?admin_tab=notifications"); + }} + onMouseEnter={() => setIsHovered("notifications")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "notifications" ? "#FF8544" : "white", + }, + }} + > + + + + Notifications ({notifications === undefined || notifications === null ? 0 : + notifications?.filter((notification) => notification.read === false).length}) + + + + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "go_to_about", + }) + } + handleCloseUserMenu(); + navigate("/docs/about"); + }} + onMouseEnter={() => setIsHovered("about")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + transition: "color 0.1s ease", + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "about" ? "#FF8544" : "white", + }, + }} + > + + + + About + + + + + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "user_dropdown_click", + label: "logout_click", + }) + } + handleCloseUserMenu(); + handleClickLogout(); + }} + onMouseEnter={() => setIsHovered("logout")} + onMouseLeave={() => setIsHovered("")} + sx={{ + py: 1.5, + px: 2, + fontFamily: theme.typography.fontFamily, + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + color: isHovered === "logout" ? "#FD4C62" : "white", + }, + }} + > + + + + Logout + + + + + + + + + Version 1.4.5 + + + + + )} + + ) : ( + <> + + + + K +
+ } + placement="bottom" + arrow + componentsProps={{ + tooltip: { + sx: { + backgroundColor: "rgba(33, 33, 33, 1)", + color: "rgba(241, 241, 241, 1)", + fontSize: 12, + border: "1px solid rgba(73, 73, 73, 1)", + fontFamily: theme?.typography?.fontFamily, + } + }, + popper: { + sx: { + zIndex: 1000019, + } + } + }} + > + { + if (isCloud) { + ReactGA.event({ + category: "navbar", + action: "search_icon_click", + label: "search_icon_click", + }) + } + setSearchBarModalOpen(true); + }} + > + + + + + + + {isCloud && + + } + + + + )} + + + )} + + + + ); +}; + +export default Navbar; From 63ce21fe6072489ab1e6109139db380fb39c5f5d Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 3 Mar 2025 22:08:06 +0100 Subject: [PATCH 044/200] Fixed linking problems --- frontend/src/components/Navbar.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 57 ++------------------------ 2 files changed, 5 insertions(+), 54 deletions(-) diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 509b9b4f..fd808894 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -178,7 +178,7 @@ const menuData = { title: "Articles", icon: "/images/icons/articles.svg", hoverIcon: "/images/icons/articles_hover.svg", - link: "/articles", + link: "/articles/2.0_release", gaData: { category: "navbar", action: "resources_click", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 5f9eadd2..79a9d98a 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1288,56 +1288,6 @@ const AngularWorkflow = (defaultprops) => { return } - //console.log("Failed in trigger selection: ", selectedTriggerIndex, "Trigger: ", selectedTrigger) - - var found = null - try { - for (var key in workflows) { - const curworkflow = workflows[key] - const curtrigger = curworkflow?.triggers[selectedTriggerIndex] - if (curtrigger === undefined || curtrigger === null) { - console.log("Failed in trigger selection (1): ", curworkflow) - continue - } - - if (curtrigger?.parameters === undefined || curtrigger?.parameters === null || curtrigger?.parameters.length === 0) { - console.log("Failed in trigger selection (2): ", curworkflow) - continue - } - - if (curtrigger?.parameters[0] === undefined || curtrigger?.parameters[0] === null || curtrigger?.parameters[0].value === undefined || curtrigger?.parameters[0].value === null) { - console.log("Failed in trigger selection (3): ", curworkflow) - continue - } - - if (curtrigger?.parameters[0].value === selectedTrigger?.parameters[0]?.value) { - found = curworkflow - setSubworkflow(curworkflow) - } - } - - if (found !== null) { - setSubworkflow(found) - } - } catch (e) { - console.log("Failed in trigger selection (4): ", e) - //return - } - - if (found) { - const startNode = found.actions?.find((action) => action.id === workflow?.triggers[selectedTriggerIndex]?.parameters[3]?.value) - setSubworkflowStartnode(startNode) - } - - /* - // Multi-tenant sometimes gives us shit :( - if (selectedTrigger === undefined || selectedTrigger === null || selectedTrigger.id === undefined || selectedTrigger.id === null && selectedTriggerIndex >= 0) { - if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length > selectedTriggerIndex) { - setSelectedTrigger(workflow.triggers[selectedTriggerIndex]) - } - } - */ - // Check if the running state is correct or not according to allTriggers if (allTriggers !== undefined && allTriggers !== null) { // Find the active trigger @@ -1592,10 +1542,10 @@ const AngularWorkflow = (defaultprops) => { if (Object.getOwnPropertyNames(baseSubflow).length > 0) { const foundAction = baseSubflow.actions.find(action => action?.id === param.value) if (foundAction !== null && foundAction !== undefined) { - setSubworkflowStartnode(foundAction); + setSubworkflowStartnode(foundAction) } } else { - setSubworkflowStartnode(param.value); + setSubworkflowStartnode(param.value) } } } @@ -3951,7 +3901,7 @@ const AngularWorkflow = (defaultprops) => { apps.push(responseJson.actions[index]); } - console.log("Setting used subflow apps: ", apps) + //console.log("Setting used subflow apps: ", apps) setUsedSubflowApps(apps); return apps @@ -15102,6 +15052,7 @@ const AngularWorkflow = (defaultprops) => { Select the Startnode
+ Date: Tue, 4 Mar 2025 19:37:44 +0100 Subject: [PATCH 045/200] Last builds for 2.0 --- backend/go-app/go.mod | 2 +- frontend/src/components/Navbar.jsx | 10 +- frontend/src/components/RuntimeDebugger.jsx | 2 +- frontend/src/views/Docs.jsx | 329 +++++++++++++++++--- frontend/src/views/Workflows2.jsx | 8 + functions/onprem/orborus/go.mod | 2 +- functions/onprem/worker/go.mod | 2 +- 7 files changed, 298 insertions(+), 57 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 9864b1dc..8af42425 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.6 + github.com/shuffle/shuffle-shared v0.8.7 golang.org/x/crypto v0.32.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.68.1 diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index fd808894..ebee1db5 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -48,7 +48,7 @@ const menuData = { description: "The most versatile automation engine with focus on security.", icon: "images/logos/orange_logo.svg", - path: "/docs", + path: "/docs/about", gaData: { category: "navbar", action: "products_click", @@ -150,7 +150,7 @@ const menuData = { title: "Documentation", icon: "/images/icons/docs.svg", hoverIcon: "/images/icons/docs_hover.svg", - link: "/docs", + link: "/docs/about", gaData: { category: "navbar", action: "resources_click", @@ -1516,7 +1516,7 @@ const Navbar = (props) => {
{/* Shuffle 1.4.0 is out! Read more about  */} - Shuffle 2.0.0 is out now! + Shuffle 2.0.0 is out now!  { ReactGA.event({ @@ -1528,7 +1528,7 @@ const Navbar = (props) => { navigate("/articles/2.0_release") }} style={{ cursor: "pointer", textDecoration: "none", fontWeight: 600, color: "rgba(255,255,255,0.9)" }}> -  Read about it here. + Read about it here. @@ -1701,6 +1701,7 @@ const Navbar = (props) => { { { imageSource = "/images/no_image.png" } }else { - if (userdata.active_org.image?.length > 0){ + if (userdata?.active_org.image?.length > 0){ imageSource = userdata?.active_org?.image }else { imageSource = "/images/no_image.png" diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 836ada35..7a9c371e 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -5,7 +5,7 @@ import theme from '../theme.jsx'; import ReactJson from "react-json-view-ssr"; import { isMobile } from "react-device-detect"; import { BrowserView, MobileView } from "react-device-detect"; -import { useParams, useNavigate, Link } from "react-router-dom"; +import { useParams, useNavigate, Link, useLocation } from "react-router-dom"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import remarkGfm from 'remark-gfm' import { Context } from "../context/ContextApi.jsx"; @@ -30,7 +30,9 @@ import { Edit as EditIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ExpandMore as ExpandMoreIcon, - FileCopy as FileCopyIcon + FileCopy as FileCopyIcon, + ChevronLeft, + ChevronRight } from "@mui/icons-material"; import { fontGrid } from "@mui/material/styles/cssUtils.js"; @@ -51,6 +53,7 @@ const dividerColor = "rgb(225, 228, 232)"; const hrefStyle = { color: "rgba(255, 255, 255, 0.8)", textDecoration: "none", + marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined, }; @@ -152,11 +155,33 @@ export const OuterLink = (props) => { } + export const Img = (props) => { // Find parent container and check width - + const isArticlePage = window.location.pathname.includes("/articles/"); var height = "auto" - var width = 750 + var width = isArticlePage ? 1000 : 750 + + const docsImageStyle = { + border: "1px solid rgba(255,255,255,0.3)", + borderRadius: theme.palette?.borderRadius, + width: width, + maxWidth: width, + margin: "auto", + marginTop: 10, + marginBottom: 10 + } + + const articleImageStyle = { + borderRadius: theme.palette?.borderRadius, + minWidth: 350, + maxWidth: "100%", + textAlign: "center", + margin: "auto", + marginTop: 20, + marginBottom: 20, + } + if (props.height !== undefined && props.height !== null) { height = props.height } @@ -167,7 +192,7 @@ export const Img = (props) => { return( {props.alt} @@ -251,8 +276,12 @@ export const CodeHandler = (props) => { } const Docs = (defaultprops) => { - const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded } = defaultprops; + const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded, userdata } = defaultprops; + + console.log("\n\nSELECTED DOC\n", selectedDoc, "\n\n") + let navigate = useNavigate(); + const location = useLocation(); // Quickfix for react router 5 -> 6 const params = useParams(); //var props = JSON.parse(JSON.stringify(defaultprops)) @@ -283,8 +312,16 @@ const Docs = (defaultprops) => { serverside === true ? "" : window.location.href ); const [hashRendered, setHashRendered] = React.useState(false) - + const [sidebarOpen, setSidebarOpen] = useState(false); + const [activeSubItem, setActiveSubItem] = useState(false); const headingElementsRef = useRef({}) + var isArticlePage = window.location.pathname.includes("/articles/") || window.location.pathname === "/articles" ? true : false; + + + useEffect(() => { + fetchDocList(); + setSidebarOpen(false); + }, [location]); useEffect(() => { //if (params["key"] === undefined) { @@ -460,8 +497,13 @@ const Docs = (defaultprops) => { } const SidebarPaperStyle = { - backgroundColor: "rgb(26,26,26)", + backgroundColor: isArticlePage ? "transparent" : "rgb(26,26,26)", + border: isArticlePage ? "none" : undefined, + borderRadius: isArticlePage ? "none" : undefined, + boxShadow: isArticlePage ? "none" : undefined, backgroundImage: "none", + width: isArticlePage ? "100%" : undefined, + height: isArticlePage ? "100%" : undefined, overflowX: "hidden", position: "relative", paddingLeft: 15, @@ -527,7 +569,7 @@ const Docs = (defaultprops) => { backgroundColor: theme.palette.inputColor, padding: 15, borderRadius: theme.palette?.borderRadius, - marginBottom: 25, + marginBottom: isArticlePage ? 25 : 30, display: "flex", }} > @@ -620,7 +662,7 @@ const Docs = (defaultprops) => { style={{ width: "90%", marginTop: 60, - marginBottom: 20, + marginBottom: isArticlePage ? 40 : 20, backgroundColor: theme.palette.inputColor, }} /> @@ -630,7 +672,21 @@ const Docs = (defaultprops) => { marginBlock: "0.85em", alignItems: "center" }}> {element} - { + e.preventDefault(); // Prevent default navigation + document.getElementById(id)?.scrollIntoView({ + behavior: 'smooth' + }); + + const url = `${window.location.origin}${window.location.pathname}#${id}`; + navigator.clipboard.writeText(url); + toast("Link copied to clipboard", { + position: "bottom-center", + autoClose: 2000, + }); + }} + style={{ textDecoration: "none", color: "white", paddingLeft: "0.3em", rotate: "-30deg", paddingTop: "0.9em", display: props.level === 1 ? "none" : "block", @@ -639,7 +695,7 @@ const Docs = (defaultprops) => {
- {extraInfo} + {extraInfo && userdata?.support}
) } @@ -647,21 +703,56 @@ const Docs = (defaultprops) => { const SideBar = { width: "17%", + borderRight: !isArticlePage ? "1px solid rgba(255,255,255,0.3)" : undefined, + minWidth: isArticlePage ? "250px" : "17%", + maxWidth: isArticlePage ? "250px" : "17%", + marginLeft: isArticlePage ? sidebarOpen ? 40 : 50 : undefined, position: "sticky", - top: 50, - paddingTop: "0.25em", - minHeight: "95vh", - maxHeight: "95vh", + top: isArticlePage ? 120 : 50, + paddingTop: isArticlePage ? "0.65em" : "0.25em", + paddingRight: isArticlePage ? "1em" : undefined, + minHeight: isArticlePage ? "80vh" : "95vh", + maxHeight: isArticlePage ? "80vh" : "95vh", overflowX: "hidden", overflowY: "auto", zIndex: 1000, - //borderRight: "1px solid rgba(255,255,255,0.3)", + backgroundColor: isArticlePage ? "#212121" : undefined, + borderRadius: isArticlePage ? theme.palette?.borderRadius : undefined, + transition: isArticlePage ? "width 0.3s ease, min-width 0.3s ease" : undefined, + }; + + const sideBarToggleButton = { + position: "absolute", + bottom: sidebarOpen ? 20 : "50%", + right: sidebarOpen ? 30 : 21, + zIndex: 1000, + backgroundColor: "#212121", + border: "1px solid rgba(255,255,255,0.3)", + borderRadius: "50%", + width: 40, + height: 40, + display: "flex", + alignItems: "center", + justifyContent: "center", + cursor: "pointer", + transition: "transform 0.3s ease", + "&:hover": { + backgroundColor: "#2c2c2c", + } + }; + + const collapsedSideBar = { + ...SideBar, + width: "40px", + minWidth: "40px", + maxWidth: "40px", + overflow: "hidden", }; const IndexBar = { alignSelf: "flex-start", position: "sticky", - top: 80, + top: 120, overflowY: "auto", minHeight: "93vh", maxHeight: "93vh", @@ -670,8 +761,9 @@ const Docs = (defaultprops) => { overflow: "hidden", } - const fetchDocList = () => { - fetch(`${globalUrl}/api/v1/docs`, { + const fetchDocList = (resetCache = false) => { + const url = isArticlePage ? `${globalUrl}/api/v1/articles?resetCache=${resetCache}` : `${globalUrl}/api/v1/docs`; + fetch(url, { method: "GET", headers: { "Content-Type": "application/json", @@ -692,7 +784,8 @@ const Docs = (defaultprops) => { }; const fetchDocs = (docId) => { - fetch(`${globalUrl}/api/v1/docs/${docId}`, { + const url = isArticlePage ? `${globalUrl}/api/v1/articles/${docId}` : `${globalUrl}/api/v1/docs/${docId}`; + fetch(url, { method: "GET", headers: { "Content-Type": "application/json", @@ -706,18 +799,32 @@ const Docs = (defaultprops) => { } if (responseJson.success && responseJson.reason !== undefined) { + + if(isArticlePage && window.location.pathname.includes("/articles/")) { + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found")) { + setData("# Error\nThis page doesn't exist."); + toast("This page doesn't exist. Redirecting to the latest article...", { + position: "bottom-center", + autoClose: 2000, + }); + setTimeout(() => { + navigate(`/articles/2.0_release`) + }, 2000) + return + } + } // Find tags and translate them into ![]() format const imgRegex = / { .catch((error) => { }); }; + const handleResetCache = () => { + fetchDocList(true); + toast("Cache has been reset"); + } + if (firstrequest) { setFirstrequest(false); if (!serverside) { @@ -769,8 +881,26 @@ const Docs = (defaultprops) => { // return null //} // - if (props.match.params.key === undefined) { - + if (props.match.params.key === undefined && isArticlePage) { + fetch(`${globalUrl}/api/v1/articles`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + }) + .then((response) => response.json()) + .then((responseJson) => { + if (responseJson.success && responseJson.list && responseJson.list.length > 0) { + // Navigate to the latest article + if (responseJson?.list[0]?.name) { + navigate(`/articles/${responseJson?.list[0].name}`); + } + } + }) + .catch((error) => { + console.error("Error fetching articles:", error); + }); } else { console.log("DOCID: ", props.match.params.key) fetchDocs(props.match.params.key) @@ -793,7 +923,7 @@ const Docs = (defaultprops) => { maxWidth: "100%", minWidth: "100%", overflow: "hidden", - fontSize: isMobile ? "1.3rem" : "1rem", + fontSize: isMobile ? "1.3rem" : "1.1rem", }; const alertNote = { @@ -940,14 +1070,38 @@ const Docs = (defaultprops) => { } + const activeHrefStyleToc2 = { + ...hrefStyleToc2, + color: "#f86a3e", + }; + + const activeListItemStyle = { + backgroundColor: "rgba(248, 106, 62, 0.08)", // Slight orange tint + marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined, + borderLeft: "3px solid #f86a3e", + paddingLeft: "13px", // Compensate for the border + }; + + const toggleSidebar = () => { + setSidebarOpen(!sidebarOpen); + }; + + // PostDataBrowser Section const postDataBrowser = list === undefined || list === null ? null : (
-
+
- + {list.map((data, index) => { const item = data.name; if (item === undefined) { @@ -965,23 +1119,62 @@ const Docs = (defaultprops) => { - {newname} - + style={{ + color: itemMatching ? "#f86a3e" : "inherit", + flex: 1, + }} + primary={ + + {newname} + + } + /> + ); })} + { + isArticlePage && ( + + {sidebarOpen ? : } + + ) + }
-
+
{props.match.params.key === undefined ? mainpageInfo : @@ -1003,12 +1196,26 @@ const Docs = (defaultprops) => { }
+ {userdata?.support && isArticlePage && ( + + )} {tocLines.length > 0 ? ( -

Table of Content

+

Table Of Content

) : null} - +
@@ -1083,7 +1297,7 @@ const Docs = (defaultprops) => { color="primary" onClick={handleClick} > -
More docs
+
More {isArticlePage ? "articles" : "docs"}
{ return null; } - const path = "/docs/" + item; + const path = isArticlePage ? "/articles/" + item : "/docs/" + item; const newname = item.charAt(0).toUpperCase() + item.substring(1).split("_").join(" ").split("-").join(" "); @@ -1108,7 +1322,8 @@ const Docs = (defaultprops) => { key={index} style={{ color: "white" }} onClick={() => { - window.location.pathname = path; + navigate(path) + handleClose() }} > {newname} @@ -1140,8 +1355,22 @@ const Docs = (defaultprops) => { { + console.log(`Scrolling to: ${data.id}`); + setTimeout(() => { + const element = document.getElementById(data?.id); + if (element) { + element.scrollIntoView({ behavior: 'smooth' }); + } else { + console.error(`Element with id ${data.id} not found.`); + } + }, 100); // Delay to ensure the content is loaded + handleCloseToc(); + }} > - {data.title} + {data.title} ) })} @@ -1178,7 +1407,7 @@ const Docs = (defaultprops) => { color="primary" onClick={handleClick} > -
More docs
+
More {isArticlePage ? "articles" : "docs"}
); @@ -1198,7 +1427,7 @@ export default Docs; const DocsContent = memo(({postDataBrowser, postDataMobile}) => { return( -
+
{postDataBrowser} {postDataMobile}
@@ -1210,12 +1439,14 @@ const DocsWrapper = memo(({isLoggedIn, isLoaded, children })=>{ return (
{children} diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 33f8618f..0b079407 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -1758,9 +1758,17 @@ const Workflows2 = (props) => { let exportFileDefaultName = data.name + ".json"; data["owner"] = ""; + data["updated_by"] = ""; data["org"] = []; data["org_id"] = ""; data["execution_org"] = {}; + data["created"] = 0 + data["due_date"] = 0 + data["edited"] = 0 + + data["validation"] = {}; + data["suborg_distribution"] = [] + data["parentorg_workflow"] = "" // These are backwards.. True = saved before. Very confuse. data["previously_saved"] = false; diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 6b8018a5..7909c2ee 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -9,7 +9,7 @@ require ( github.com/docker/docker v27.5.0+incompatible github.com/docker/go-connections v0.5.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.6 + github.com/shuffle/shuffle-shared v0.8.7 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 ) diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 01cad7bf..5b8cbd22 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -8,7 +8,7 @@ require ( github.com/docker/docker v27.5.0+incompatible github.com/gorilla/mux v1.8.1 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.6 + github.com/shuffle/shuffle-shared v0.8.7 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 k8s.io/client-go v0.30.2 From 6a2355c6f6b55a8b0b8ad303e69ce95fa06793d2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 4 Mar 2025 21:56:39 +0100 Subject: [PATCH 046/200] Minor frontend fixes --- frontend/src/components/AppCreationModal.jsx | 40 ++++++++++---------- frontend/src/views/Apps2.jsx | 1 + 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/AppCreationModal.jsx b/frontend/src/components/AppCreationModal.jsx index 80cf1711..a9f8f598 100644 --- a/frontend/src/components/AppCreationModal.jsx +++ b/frontend/src/components/AppCreationModal.jsx @@ -22,7 +22,6 @@ import CreateIcon from '@mui/icons-material/Create' import { toast } from 'react-toastify' import YAML from "yaml"; - const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { const [openApiModal, setOpenApiModal] = useState(false) const [generateAppModal, setGenerateAppModal] = useState(false) @@ -257,26 +256,27 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { body: openApidata, credentials: "include", }) - .then((response) => { + .then((response) => { - setValidation(false); - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success) { - setAppValidation(responseJson.id); - } else { - if (responseJson.reason !== undefined) { - setOpenApiError(responseJson.reason); - } - toast("An error occurred in the response"); - } - }) - .catch((error) => { - setValidation(false); - toast(error.toString()); - setOpenApiError(error.toString()); - }); + setValidation(false); + return response.json(); + }) + .then((responseJson) => { + if (responseJson?.success === true) { + setAppValidation(responseJson?.id) + navigate(`/apps/new?id=${responseJson?.id}`) + } else { + if (responseJson.reason !== undefined) { + setOpenApiError(responseJson.reason) + } + toast("An error occurred in the response"); + } + }) + .catch((error) => { + setValidation(false); + toast(error.toString()); + setOpenApiError(error.toString()); + }); }; const redirectOpenApi = () => { diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index 87e60f82..c5fb9d38 100644 --- a/frontend/src/views/Apps2.jsx +++ b/frontend/src/views/Apps2.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useContext, useCallback, memo, useMemo, useRef } from "react"; import theme from "../theme.jsx"; import { isMobile } from "react-device-detect"; + import { useLocation, useNavigate } from "react-router-dom"; import { TextField, Button, Typography, MenuItem, Select, Tabs, Tab, Zoom, From bc13fc882a8c11e187d160929057a3327b5605de Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 5 Mar 2025 13:49:57 +0100 Subject: [PATCH 047/200] Rebuild with 2.0.0 images for consistency --- .github/workflows/dockerbuild.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 570c21f1..85890765 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -20,19 +20,19 @@ jobs: include: - app: frontend path: frontend - version: latest + version: 2.0.0 experimental: true - app: backend path: backend - version: latest + version: 2.0.0 experimental: true - app: orborus path: functions/onprem/orborus - version: latest + version: 2.0.0 experimental: true - app: worker path: functions/onprem/worker - version: latest + version: 2.0.0 experimental: true steps: - name: Checkout From 6598fe7cfed08121a61f40277059b19d8ab775ea Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 5 Mar 2025 14:09:13 +0100 Subject: [PATCH 048/200] Fixed an issue where SSO URL was not being parsed on the login page --- frontend/src/context/ContextApi.jsx | 13 +++++++++---- frontend/src/views/Docs.jsx | 3 +-- frontend/src/views/LoginPage.jsx | 28 ++++++++++++++++++++++++++++ frontend/src/views/LoginPageOld.jsx | 2 +- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/frontend/src/context/ContextApi.jsx b/frontend/src/context/ContextApi.jsx index 3932dfd0..4e82ba1e 100644 --- a/frontend/src/context/ContextApi.jsx +++ b/frontend/src/context/ContextApi.jsx @@ -1,14 +1,15 @@ -import { createContext, useState, useEffect } from 'react'; +import React, { createContext, useState, useEffect } from 'react'; export const Context = createContext(); -export const AppContext =(props) => { +export const AppContext = (props) => { + const { serverside } = props - const currentLocation = window?.location?.pathname; + const currentLocation = serverside === true ? "" : window?.location?.pathname; // Left side bar global states const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true) - const [windowWidth, setWindowWidth] = useState(window.innerWidth); + const [windowWidth, setWindowWidth] = useState(serverside === true ? 100 : window.innerWidth); useEffect(() => { if (currentLocation?.includes('/workflows/') && leftSideBarOpenByClick === true) { @@ -18,6 +19,10 @@ export const AppContext =(props) => { //Calculate window width useEffect(() => { + if (serverside === true) { + return + } + const handleResize = () => { setWindowWidth(window?.innerWidth); }; diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 7a9c371e..6aa8ec08 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -694,8 +694,7 @@ const Docs = (defaultprops) => {
- - {extraInfo && userdata?.support} + {isArticlePage ? (userdata?.support ? extraInfo : "") : extraInfo} ) } diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 02a2967f..385a2d8e 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -282,6 +282,8 @@ const LoginPage = props => { const [register, setRegister] = useState(inregister); const [checkboxClicked, setCheckboxClicked] = useState(false); const [loginWithSSO, setLoginWithSSO] = useState(false) + + const [ssoUrl, setSSOUrl] = useState(""); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io"; const parsedsearch = serverside === true ? "" : window.location.search @@ -438,6 +440,9 @@ const LoginPage = props => { if (responseJson["success"] === false) { setLoginInfo(responseJson["reason"]); } else { + if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { + setSSOUrl(responseJson.sso_url); + } // Stay = 0 users // Redirect = >1 user @@ -901,6 +906,29 @@ const LoginPage = props => {
{loginInfo}
+ + {ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? ( +
+ Or +
+ +
+
+ ) : null} diff --git a/frontend/src/views/LoginPageOld.jsx b/frontend/src/views/LoginPageOld.jsx index 6b34b957..541cd903 100755 --- a/frontend/src/views/LoginPageOld.jsx +++ b/frontend/src/views/LoginPageOld.jsx @@ -498,7 +498,7 @@ const LoginDialog = (props) => {
+
{top_text === "Base Cloud Access" && userdata.has_card_available === true ? { { }} @@ -2013,7 +2016,33 @@ const Billing = memo((props) => { : null} -
+
+
+ {isCloud && + selectedOrganization.subscriptions !== undefined && + selectedOrganization.subscriptions !== null && + selectedOrganization.subscriptions.length > 0 && + !isChildOrg ? + selectedOrganization.subscriptions + .reverse() + .map((sub, index) => { + return ( + + ) + }) + : null} + +
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null : { isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} + isScale={isScale} {...props} /> : !isCloud ? @@ -2061,6 +2091,7 @@ const Billing = memo((props) => { isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} + isScale={isScale} /> {/* { /> */} : null} +
+
- {isCloud && - selectedOrganization.subscriptions !== undefined && - selectedOrganization.subscriptions !== null && - selectedOrganization.subscriptions.length > 0 && - !isChildOrg ? - selectedOrganization.subscriptions - .reverse() - .map((sub, index) => { - return ( - - ) - }) - : null} {/* { */}
- {/*isCloud && selectedOrganization.partner_info !== undefined && selectedOrganization.partner_info.reseller === true ? ( diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index b67bde93..48574126 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -577,12 +577,20 @@ const EditWorkflow = (props) => { Multi-Tenancy, Backups & Security - - Multi-Tenant Workflows. Make one workflow, and keep a separate, synced copy in all your tenants. Control distributed auth, runtime locations, files, datastore keys etc. (contact support@shuffler.io if you want a demo. Please try it!) + Control mechanisms for multi-tenancy, backups, and security. - {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? + + Multi-Tenant Workflows + + + + Make one workflow, and keep a separate, synced copy in your other tenants. Control distributed auth, runtime locations, files, datastore keys etc. Can only distribute from parent org to child org. Need help trying it? Contact us for a demo + + + {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? + userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ? userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ? @@ -600,6 +608,7 @@ const EditWorkflow = (props) => { multiple style={{ marginTop: 10, }} value={innerWorkflow.suborg_distribution === undefined || innerWorkflow.suborg_distribution === null ? ["none"] : innerWorkflow.suborg_distribution} + disabled={workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0} onChange={(e) => { var newvalue = e.target.value if (newvalue.length > 1 && newvalue[0] === "none") { diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index f52ffa45..9492ae99 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -46,7 +46,7 @@ import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" import Billing from "./Billing.jsx"; const LicencePopup = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isScale, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; //const alert = useAlert(); let navigate = useNavigate(); const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); @@ -165,11 +165,25 @@ const LicencePopup = (props) => { ] } + if (userdata?.app_execution_limit >= 300000) { + subscription.name = "Enterprise" + subscription.currency_text = "$" + subscription.price = typecost_single + subscription.limit = userdata?.app_execution_limit + subscription.interval = "app run / month" + subscription.features = [ + "Includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. ", + "Multi-Tenancy and Region-Selection", + "And all other features from /pricing", + ] + } + + var newPaperstyle = JSON.parse(JSON.stringify(paperStyle)) if (subscription.name === "Enterprise" && subscription.active === true) { - top_text = "Current Plan" + top_text = "Enterprise Plan" - newPaperstyle.border = "1px solid #f85a3e" + // newPaperstyle.border = "1px solid #f85a3e" } var showSupport = false @@ -255,7 +269,7 @@ const LicencePopup = (props) => { style={{ borderRadius: theme.palette?.borderRadius, }} placement="bottom" > -
+
setHovered(true)} @@ -343,9 +357,9 @@ const LicencePopup = (props) => {
- + {subscription.active === true && !isScale && }
- {top_text === "Base Cloud Access" && userdata.has_card_available === true ? + {top_text === "Base Cloud Access" && userdata.has_card_available === true && !isScale ? { "While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month." : isCloud ? - `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` + userdata?.app_execution_limit && userdata?.app_execution_limit >= 300000 ? + "You have subscribed to the Enterprise plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." : + `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` + : `You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.` } @@ -699,8 +716,33 @@ const LicencePopup = (props) => { setCalculatedCost("$960") setSelectedValue(8) } else { - setCalculatedCost("$960") - setSelectedValue(300) + if (userdata && userdata?.app_execution_limit) { + if (userdata.app_execution_limit >= 300000 && userdata.app_execution_limit < 400000) { + setSelectedValue(400) + setCalculatedCost("$1280") + }else if (userdata?.app_execution_limit >= 400000 && userdata?.app_execution_limit < 500000) { + setSelectedValue(500) + setCalculatedCost("$1600") + } else if (userdata?.app_execution_limit >= 500000 && userdata?.app_execution_limit < 600000) { + setSelectedValue(600) + setCalculatedCost("$1920") + } else if (userdata?.app_execution_limit >= 600000 && userdata?.app_execution_limit < 700000) { + setSelectedValue(700) + setCalculatedCost("$2240") + } else if (userdata?.app_execution_limit >= 700000 && userdata?.app_execution_limit < 800000) { + setSelectedValue(800) + setCalculatedCost("$2560") + } else if (userdata?.app_execution_limit >= 800000 && userdata?.app_execution_limit < 900000) { + setSelectedValue(900) + setCalculatedCost("$2880") + }else { + setCalculatedCost("$960") + setSelectedValue(300) + } + }else { + setCalculatedCost("$960") + setSelectedValue(300) + } } }, [shuffleVariant]) @@ -977,7 +1019,7 @@ const LicencePopup = (props) => { : null} - {isCloud && + {/* {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ? @@ -998,7 +1040,7 @@ const LicencePopup = (props) => { /> ) }) - : null} + : null} */} @@ -1006,12 +1048,12 @@ const LicencePopup = (props) => {
- + color="primary">Recommended }
{shuffleVariant === 1 ? "Scale" : "Enterprise"} diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index ebee1db5..c3cca3f8 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -47,7 +47,7 @@ const menuData = { title: "Shuffle", description: "The most versatile automation engine with focus on security.", - icon: "images/logos/orange_logo.svg", + icon: "images/icons/shuffleLogo.svg", path: "/docs/about", gaData: { category: "navbar", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 79a9d98a..374ce85b 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -575,11 +575,17 @@ const AngularWorkflow = (defaultprops) => { }, [editWorkflowModalOpen]) useEffect(() => { - if(selectedTrigger?.trigger_type === "SUBFLOW"){ + // Check if selectedTriggerIndex is a number >= 0 + if (isNaN(selectedTriggerIndex) || selectedTriggerIndex < 0) { + return + } + + if (selectedTrigger?.trigger_type === "SUBFLOW" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 1) { setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[1].value || "") - } else if(selectedTrigger?.trigger_type === "USERINPUT"){ + } else if (selectedTrigger?.trigger_type === "USERINPUT" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 0) { setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[0].value || "Do you want to continue the workflow? Start parameters: $exec") } + }, [selectedTriggerIndex, selectedTrigger, workflow]) useEffect(() => { @@ -948,11 +954,15 @@ const AngularWorkflow = (defaultprops) => { } useEffect(() => { - if (workflow.actions?.length == 1) { - if (workflow.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") { + if (workflow?.actions?.length == 1) { + if (workflow?.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") { setWorkflowAsCode(true); } } + + if (workflow?.suborg_distribution !== undefined && workflow?.suborg_distribution !== null && workflow?.suborg_distribution.length > 0) { + getChildWorkflows(workflow.id) + } }, [workflow]); // Event for making sure app is correct @@ -1495,6 +1505,8 @@ const AngularWorkflow = (defaultprops) => { if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { headers["Org-Id"] = workflow.org_id } + + setWorkflows([]) fetch(globalUrl + "/api/v1/workflows?subflow=true", { method: "GET", @@ -1512,7 +1524,7 @@ const AngularWorkflow = (defaultprops) => { if (responseJson !== undefined) { // Sets up subflow trigger with the right info - if (trigger_index > -1) { + if (isNaN(trigger_index) === false && trigger_index > -1) { var baseSubflow = {} const trigger = workflow.triggers[trigger_index]; @@ -2494,15 +2506,19 @@ const AngularWorkflow = (defaultprops) => { if (cy !== undefined && cy !== null) { // scale: 0.3, // bg: "#27292d", - if (cy.png !== undefined && cy.png !== null) { - const cyImageData = cy.png({ - output: "base64uri", - maxWidth: 480, - maxHeight: 270, - }) + if (cy?.png !== undefined && cy?.png !== null) { + try { + const cyImageData = cy.png({ + output: "base64uri", + maxWidth: 480, + maxHeight: 270, + }) - if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { - useworkflow.image = cyImageData + if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { + useworkflow.image = cyImageData + } + } catch (e) { + console.log("Failed to get image data: ", e) } } } @@ -3914,6 +3930,10 @@ const AngularWorkflow = (defaultprops) => { } const findWorkflowDiff = (parentWorkflow, childWorkflow) => { + // Ensures new memory is used + parentWorkflow = JSON.parse(JSON.stringify(parentWorkflow)) + childWorkflow = JSON.parse(JSON.stringify(childWorkflow)) + var diff = { "different": false, "environment": false, @@ -3931,9 +3951,13 @@ const AngularWorkflow = (defaultprops) => { return diff } - var parentEnvironment = "" var childEnvironment = "" + + if (parentWorkflow.triggers !== undefined && parentWorkflow.triggers !== null && parentWorkflow.triggers.length > 0) { + parentWorkflow.actions = parentWorkflow.actions.concat(parentWorkflow.triggers) + } + for (var parentKey in parentWorkflow.actions) { const parentAction = parentWorkflow.actions[parentKey] if (parentAction.environment !== undefined && parentAction.environment !== null && parentAction.environment !== "") { @@ -3945,6 +3969,19 @@ const AngularWorkflow = (defaultprops) => { } var found = false + if (childWorkflow.triggers !== undefined && childWorkflow.triggers !== null && childWorkflow.triggers.length > 0) { + for (var triggerKey in childWorkflow.triggers) { + if (childWorkflow.triggers[triggerKey].replacement_for_trigger !== parentAction.id) { + continue + } + + // Rewrapping the ID just in case + childWorkflow.triggers[triggerKey].id = childWorkflow.triggers[triggerKey].replacement_for_trigger + childWorkflow.actions.push(childWorkflow.triggers[triggerKey]) + break + } + } + for (var childKey in childWorkflow.actions) { const childAction = childWorkflow.actions[childKey] if (childAction.environment !== undefined && childAction.environment !== null && childAction.environment !== "") { @@ -3991,6 +4028,10 @@ const AngularWorkflow = (defaultprops) => { continue } + var parentworkflow = "" + var parentstartnode = "" + var childworkflow = "" + var childstartnode = "" for (var parentParamIndex in parentAction.parameters) { const parentParam = parentAction.parameters[parentParamIndex] for (var childParamIndex in childAction.parameters) { @@ -3999,20 +4040,38 @@ const AngularWorkflow = (defaultprops) => { continue } + if (childParam.name === "workflow" || childParam.name === "subflow") { + parentworkflow = parentParam.value + childworkflow = childParam.value + continue + } + + if (childParam.name == "startnode") { + parentstartnode = parentParam.value + childstartnode = childParam.value + continue + } + if (childParam.value !== parentParam.value) { actionDiff.parameters.push(childParam.name) } } } + + if (parentworkflow !== childworkflow && parentstartnode !== childstartnode) { + actionDiff.parameters.push("subflow") + } } if (actionDiff.parameters.length > 0) { actionDiff.params = true } + /* if (!found) { actionDiff.new = true } + */ if (actionDiff !== undefined && actionDiff !== null && Object.keys(actionDiff).length > 1) { actionDiff.label = parentAction.label.replaceAll("_", " ") @@ -6305,11 +6364,15 @@ const AngularWorkflow = (defaultprops) => { if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0 && originalWorkflow.org_id !== undefined && originalWorkflow.org_id !== null && originalWorkflow.org_id.length > 0 && workflow.org_id === originalWorkflow.org_id) { // Allows a parent workflow to control the schedule } else if (data.replacement_for_trigger !== undefined && data.replacement_for_trigger !== null && data.replacement_for_trigger.length > 0) { - toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", { - autoClose: 30000, - }) - event.target.unselect() - return + + // No custom control on cloud + if (isCloud) { + toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", { + autoClose: 30000, + }) + event.target.unselect() + return + } } } else if (data.app_name === "Webhook" && trigger_index >= 0) { @@ -6885,7 +6948,7 @@ const AngularWorkflow = (defaultprops) => { ) if (targetnode !== -1) { if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") { - console.log("User Input or Shuffle Workflow") + //console.log("User Input or Shuffle Workflow") } else { toast("Can't have triggers as target of branch") event.target.remove() @@ -9217,11 +9280,15 @@ const AngularWorkflow = (defaultprops) => { } insertedNodes = insertedNodes.concat(newedges); - setWorkflow(inputworkflow); + setWorkflow(inputworkflow) // Reset view for cytoscape if (cy !== undefined && cy !== null) { - cy.add(insertedNodes) + try { + cy.add(insertedNodes) + } catch (error) { + console.log("Error adding nodes to cytoscape (6): ", error) + } try { cy.fit(null, 250) @@ -9300,12 +9367,6 @@ const AngularWorkflow = (defaultprops) => { } } - //if (selectedNode.data("id") === selectedAction.id) { - // setSelectedApp({}); - // setSelectedAction({}); - //setSelectedTrigger({}); - //setSelectedTriggerIndex({}); - //} const parsedSelection = cy.$(":selected"); if (selectedNode.data().decorator === true && selectedNode.data("type") !== "COMMENT") { toast("This node can't be deleted."); @@ -9505,7 +9566,6 @@ const AngularWorkflow = (defaultprops) => { if (firstrequest) { setFirstrequest(false) getWorkflow(props.match.params.key, {}) - getChildWorkflows(props.match.params.key) getRevisionHistory(props.match.params.key) loadTriggers() getApps() @@ -14891,12 +14951,15 @@ const AngularWorkflow = (defaultprops) => { }} >
- Select a workflow to execute + Select a workflow to run
+ {workflow.triggers[selectedTriggerIndex].parameters[0].value .length === 0 ? null : workflow.triggers[selectedTriggerIndex] - .parameters[0].value === props.match.params.key ? null : ( + .parameters[0].value === props.match.params.key ? + null + : (
{ {workflows === undefined || workflows === null || - workflows.length === 0 ? null : ( - + workflows.length === 0 ? + + : ( { value={workflow.org_id} disabled={savingState !== 0 || suborgWorkflows?.length === 0 || allTriggers === undefined} onChange={(e) => { + if (cy !== undefined && cy !== null) { + cy.nodes().unselect() + } + if (lastSaved === false && originalWorkflow.id === workflow.id) { setSuborgWorkflows([]) diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 385a2d8e..97c8b5cf 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -427,14 +427,14 @@ const LoginPage = props => { window.location.pathname = "/workflows" } - const checkAdmin = () => { - const url = globalUrl + "/api/v1/checkusers"; - fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) + const checkAdmin = () => { + const url = globalUrl + "/api/v1/checkusers"; + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { @@ -459,7 +459,7 @@ const LoginPage = props => { navigate("/adminsetup") }, 2500) }) - }; + } const onSubmit = (e) => { @@ -952,7 +952,7 @@ const LoginPage = props => {
return ( -
+
{loadedCheck}
) diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 1eee8317..100eed13 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1946,7 +1946,10 @@ const Workflows = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); - toast("Failed deleting workflow. Do you have access?"); + + if (bulk !== true) { + toast(`Failed deleting workflow ${id}. Do you have access?`); + } } else { if (bulk !== true) { toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`) diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 0b079407..f1e18120 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -2043,7 +2043,10 @@ const Workflows2 = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); - toast("Failed deleting workflow. Do you have access?"); + + if (bulk !== true) { + toast("Failed deleting workflow. Do you have access?"); + } } else { if (bulk !== true) { toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`) @@ -2821,7 +2824,7 @@ const Workflows2 = (props) => { {(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") && type !== "public" ? -
+
{ : null} {(data?.validation?.validation_ran === true && data?.validation?.valid === false && data?.validation?.errors?.length > 0 ) ? - -
+ +
{
- : null} +
@@ -3235,6 +3238,7 @@ const Workflows2 = (props) => {
} + Date: Tue, 11 Mar 2025 16:21:50 +0530 Subject: [PATCH 054/200] Skip runnig frontend testing for halth api check in github action --- .github/workflows/quick-testing.yml | 46 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 13d2c89e..cd34ea75 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -63,12 +63,12 @@ jobs: exit 1 fi - - name: Run Selenium testing for frontend - run: | - cd $GITHUB_WORKSPACE/frontend - # write some log to see the current directory - chmod +x frontend-testing.sh - ./frontend-testing.sh + # - name: Run Selenium testing for frontend + # run: | + # cd $GITHUB_WORKSPACE/frontend + # # write some log to see the current directory + # chmod +x frontend-testing.sh + # ./frontend-testing.sh - name: Get the API key and run a health check id: health_check @@ -113,21 +113,21 @@ jobs: echo "Health endpoint check failed. Response did not meet expected criteria." exit 1 fi - notify: - needs: build - if: failure() - runs-on: ubuntu-latest - steps: - - name: Send Twilio Alert - run: | - WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - MESSAGE="🚨 Shuffle Workflow Failed! - Repository: ${{ github.repository }} - Branch: ${{ github.ref_name }} - Workflow URL: $WORKFLOW_URL" + # notify: + # needs: build + # if: failure() + # runs-on: ubuntu-latest + # steps: + # - name: Send Twilio Alert + # run: | + # WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + # MESSAGE="🚨 Shuffle Workflow Failed! + # Repository: ${{ github.repository }} + # Branch: ${{ github.ref_name }} + # Workflow URL: $WORKFLOW_URL" - curl -s -X POST https://api.twilio.com/2010-04-01/Accounts/${{ secrets.TWILIO_ACCOUNT_SID }}/Messages.json \ - --data-urlencode "To=${{ secrets.TWILIO_TO_NUMBER }}" \ - --data-urlencode "From=${{ secrets.TWILIO_FROM_NUMBER }}" \ - --data-urlencode "Body=$MESSAGE" \ - -u "${{ secrets.TWILIO_ACCOUNT_SID }}:${{ secrets.TWILIO_AUTH_TOKEN }}" > /dev/null + # curl -s -X POST https://api.twilio.com/2010-04-01/Accounts/${{ secrets.TWILIO_ACCOUNT_SID }}/Messages.json \ + # --data-urlencode "To=${{ secrets.TWILIO_TO_NUMBER }}" \ + # --data-urlencode "From=${{ secrets.TWILIO_FROM_NUMBER }}" \ + # --data-urlencode "Body=$MESSAGE" \ + # -u "${{ secrets.TWILIO_ACCOUNT_SID }}:${{ secrets.TWILIO_AUTH_TOKEN }}" > /dev/null From 25ca1e7e2fd74386bbbc7d8e18a1c147c4ef67e7 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 16:26:51 +0530 Subject: [PATCH 055/200] Fix - double workflow dispatch line --- .github/workflows/quick-testing.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index cd34ea75..c8e04cdc 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -6,7 +6,6 @@ on: types: - completed workflow_dispatch: - workflow_dispatch: jobs: build: From e16f3adffcc0ce506bb062bc60bd8be899aedda6 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 16:44:59 +0530 Subject: [PATCH 056/200] declared env value --- .github/workflows/quick-testing.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index c8e04cdc..779ef852 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -15,6 +15,11 @@ jobs: os: [ubuntu-latest] architecture: [x64, arm64] + env: + SHUFFLE_DEFAULT_USERNAME: demo@demo.io + SHUFFLE_DEFAULT_PASSWORD: supercoolpassword + SHUFFLE_DEFAULT_APIKEY: supercoolapikey + steps: - name: Checkout code uses: actions/checkout@v2 @@ -72,10 +77,13 @@ jobs: - name: Get the API key and run a health check id: health_check run: | - RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search') + RESPONSE=$(curl -s -k -u "$SHUFFLE_DEFAULT_USERNAME:$SHUFFLE_DEFAULT_PASSWORD" 'https://localhost:9200/users/_search') + echo "Raw Response: $RESPONSE" + API_KEY=$(echo "$RESPONSE" | jq -r '.hits.hits[0]._source.apikey') - if [ -n "$API_KEY" ]; then + if [ -n "$API_KEY" ] && [ "$API_KEY" != "null" ]; then echo "Admin API key: $API_KEY" + echo "API_KEY=$API_KEY" >> $GITHUB_ENV else echo "Failed to retrieve the API key for the admin user." exit 1 From 338f62c9e12b1c7736ff11492efaf90bbd77ba5b Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 16:58:00 +0530 Subject: [PATCH 057/200] Don't create new user by env variable --- .github/workflows/quick-testing.yml | 47 ++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 779ef852..c0037617 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -14,12 +14,6 @@ jobs: matrix: os: [ubuntu-latest] architecture: [x64, arm64] - - env: - SHUFFLE_DEFAULT_USERNAME: demo@demo.io - SHUFFLE_DEFAULT_PASSWORD: supercoolpassword - SHUFFLE_DEFAULT_APIKEY: supercoolapikey - steps: - name: Checkout code uses: actions/checkout@v2 @@ -66,6 +60,45 @@ jobs: echo "The word 'Shuffle' was not found in the response." exit 1 fi + - name: Register a user and check the status code + run: | + MAX_RETRIES=30 + RETRY_INTERVAL=10 + CONTAINER_NAME="shuffle-backend" + + for (( i=1; i<=$MAX_RETRIES; i++ )) + do + STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" 'http://localhost:3001/api/v1/register' \ + -H 'Accept: */*' \ + -H 'Accept-Language: en-US,en;q=0.9' \ + -H 'Connection: keep-alive' \ + -H 'Content-Type: application/json' \ + --data-raw '{"username":"demo@demo.io","password":"supercoolpassword"}') + + if [ "$STATUS_CODE" -eq 200 ]; then + echo "User registration was successful with status code 200." + exit 0 + elif [ "$STATUS_CODE" -ne 502 ]; then + echo "User registration failed with status code $STATUS_CODE." + exit 1 + fi + + echo "Received status code $STATUS_CODE. Retrying in $RETRY_INTERVAL seconds... ($i/$MAX_RETRIES)" + echo "Fetching last 30 lines of logs from container $CONTAINER_NAME..." + + logs_output=$(docker logs --tail 30 "$CONTAINER_NAME") + echo "$logs_output" + + echo "Fetching last 30 lines of logs from container shuffle-opensearch..." + + opensearch_logs=$(docker logs --tail 30 shuffle-opensearch) + echo "$opensearch_logs" + + sleep $RETRY_INTERVAL + done + + echo "User registration failed after $MAX_RETRIES attempts." + exit 1 # - name: Run Selenium testing for frontend # run: | @@ -77,7 +110,7 @@ jobs: - name: Get the API key and run a health check id: health_check run: | - RESPONSE=$(curl -s -k -u "$SHUFFLE_DEFAULT_USERNAME:$SHUFFLE_DEFAULT_PASSWORD" 'https://localhost:9200/users/_search') + RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search') echo "Raw Response: $RESPONSE" API_KEY=$(echo "$RESPONSE" | jq -r '.hits.hits[0]._source.apikey') From 14eec0313965bb967b68509a04d673dd448708a6 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 22:58:35 +0530 Subject: [PATCH 058/200] check if health api job runs on 5001 port --- .github/workflows/quick-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index c0037617..d65da379 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -122,7 +122,7 @@ jobs: exit 1 fi - HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \ + HEALTH_RESPONSE=$(curl -s 'http://localhost:5001/api/v1/health?force=true' \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \ -H 'Accept-Language: en-US,en;q=0.9' \ -H 'Connection: keep-alive' \ From 6b5c2a7517739f3b46a63d25bada1494a678fbbd Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 23:19:24 +0530 Subject: [PATCH 059/200] change backend port for health api --- .github/workflows/quick-testing.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index d65da379..55ca6122 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -122,7 +122,7 @@ jobs: exit 1 fi - HEALTH_RESPONSE=$(curl -s 'http://localhost:5001/api/v1/health?force=true' \ + HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \ -H 'Accept-Language: en-US,en;q=0.9' \ -H 'Connection: keep-alive' \ @@ -153,6 +153,12 @@ jobs: echo "Health endpoint check failed. Response did not meet expected criteria." exit 1 fi + - name: Get shuffle-orborus logs if health check fails + if: failure() + run: | + echo "Health check failed. Fetching shuffle-orborus logs..." + docker logs --tail 50 shuffle-orborus + # notify: # needs: build # if: failure() From 0f039d65bbe53d1bb91a488cd9c8332f42ed86df Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 23:27:40 +0530 Subject: [PATCH 060/200] print response of health api --- .github/workflows/quick-testing.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 55ca6122..6770d5c8 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -147,6 +147,12 @@ jobs: WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status') WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete') + echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE" + echo "WORKFLOWS_RUN: $WORKFLOWS_RUN" + echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED" + echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS" + echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE" + if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then echo "Health endpoint check was successful." else From 9e9c329bf94dbb4ae95ffea8113476e0490773a7 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 23:36:14 +0530 Subject: [PATCH 061/200] Try multiple time for health api response --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index b3c4dbdb..78bdad12 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,6 +47,7 @@ services: - ENVIRONMENT_NAME=Shuffle - ORG_ID=Shuffle - BASE_URL=http://${OUTER_HOSTNAME}:5001 + # - BASE_URL=http://192.168.1.4:5001 - DOCKER_API_VERSION=1.40 - HTTP_PROXY=${HTTP_PROXY} - HTTPS_PROXY=${HTTPS_PROXY} From e283785a4799b67403a6bc3721d0d5c3def691b7 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 23:41:16 +0530 Subject: [PATCH 062/200] Try multiple time for health api response --- .github/workflows/quick-testing.yml | 68 +++++++++++++++-------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 6770d5c8..9e12f73c 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -122,43 +122,45 @@ jobs: exit 1 fi - HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \ - -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \ - -H 'Accept-Language: en-US,en;q=0.9' \ - -H 'Connection: keep-alive' \ - -H 'Sec-Fetch-Dest: document' \ - -H 'Sec-Fetch-Mode: navigate' \ - -H 'Sec-Fetch-Site: none' \ - -H "Authorization: Bearer $API_KEY" \ - -H 'Sec-Fetch-User: ?1' \ - -H 'Upgrade-Insecure-Requests: 1' \ - -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \ - -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \ - -H 'sec-ch-ua-mobile: ?0' \ - -H 'sec-ch-ua-platform: "macOS"') + MAX_RETRIES=5 # Total attempts (1 initial + 4 retries) + RETRY_INTERVAL=10 # Retry every 10 seconds after first failure - echo "health_response<> $GITHUB_ENV - echo "$HEALTH_RESPONSE" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV + echo "Waiting 1 minute before the first health check..." + sleep 60 # Initial wait before first attempt - WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create') - WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run') - WORKFLOWS_RUN_FINISHED=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_finished') - WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status') - WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete') + for ((i=1; i<=$MAX_RETRIES; i++)); do + echo "Attempt $i/$MAX_RETRIES: Checking health API..." + + HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \ + -H "Authorization: Bearer $API_KEY") - echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE" - echo "WORKFLOWS_RUN: $WORKFLOWS_RUN" - echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED" - echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS" - echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE" + echo "Health API Response: $HEALTH_RESPONSE" - if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then - echo "Health endpoint check was successful." - else - echo "Health endpoint check failed. Response did not meet expected criteria." - exit 1 - fi + WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create') + WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run') + WORKFLOWS_RUN_FINISHED=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_finished') + WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status') + WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete') + + echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE" + echo "WORKFLOWS_RUN: $WORKFLOWS_RUN" + echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED" + echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS" + echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE" + + if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then + echo "Health endpoint check was successful." + exit 0 + fi + + if [ "$i" -lt "$MAX_RETRIES" ]; then + echo "Health check failed. Retrying in $RETRY_INTERVAL seconds..." + sleep $RETRY_INTERVAL + fi + done + + echo "Health check failed after $MAX_RETRIES attempts." + exit 1 - name: Get shuffle-orborus logs if health check fails if: failure() run: | From 16b1adc492fc408e0df2ed8ce0daa910de30ee62 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 23:41:38 +0530 Subject: [PATCH 063/200] Revert "Try multiple time for health api response" This reverts commit 9e9c329bf94dbb4ae95ffea8113476e0490773a7. --- docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 78bdad12..b3c4dbdb 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,6 @@ services: - ENVIRONMENT_NAME=Shuffle - ORG_ID=Shuffle - BASE_URL=http://${OUTER_HOSTNAME}:5001 - # - BASE_URL=http://192.168.1.4:5001 - DOCKER_API_VERSION=1.40 - HTTP_PROXY=${HTTP_PROXY} - HTTPS_PROXY=${HTTPS_PROXY} From 1cde0660720a6d47e11b42e3b700b75e606ac645 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 11 Mar 2025 23:50:25 +0530 Subject: [PATCH 064/200] Fix - env file and testing workflow --- .env | 6 +++--- .github/workflows/quick-testing.yml | 17 ++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/.env b/.env index 457f9882..53204db2 100755 --- a/.env +++ b/.env @@ -19,9 +19,9 @@ SHUFFLE_DOWNLOAD_AUTH_BRANCH= SHUFFLE_APP_FORCE_UPDATE=false # User config for first load. Username & PW: min length 3 -SHUFFLE_DEFAULT_USERNAME="demo@demo.io" -SHUFFLE_DEFAULT_PASSWORD="supercoolpassword" -SHUFFLE_DEFAULT_APIKEY="supercoolapikey" +SHUFFLE_DEFAULT_USERNAME= +SHUFFLE_DEFAULT_PASSWORD= +SHUFFLE_DEFAULT_APIKEY= # Local location of your app directory. Can't use ~/ # Files will get better at some point. Right now: local saving. diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 9e12f73c..6af52777 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -100,12 +100,12 @@ jobs: echo "User registration failed after $MAX_RETRIES attempts." exit 1 - # - name: Run Selenium testing for frontend - # run: | - # cd $GITHUB_WORKSPACE/frontend - # # write some log to see the current directory - # chmod +x frontend-testing.sh - # ./frontend-testing.sh + - name: Run Selenium testing for frontend + run: | + cd $GITHUB_WORKSPACE/frontend + # write some log to see the current directory + chmod +x frontend-testing.sh + ./frontend-testing.sh - name: Get the API key and run a health check id: health_check @@ -161,11 +161,6 @@ jobs: echo "Health check failed after $MAX_RETRIES attempts." exit 1 - - name: Get shuffle-orborus logs if health check fails - if: failure() - run: | - echo "Health check failed. Fetching shuffle-orborus logs..." - docker logs --tail 50 shuffle-orborus # notify: # needs: build From 46cb4978edaab9a17c84d62a314956fedb3886c3 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Wed, 12 Mar 2025 00:40:51 +0530 Subject: [PATCH 065/200] Fix - selenium script issue and dashbaord page crash issue --- frontend/selenium-test.js | 7 +++++-- frontend/src/views/DashboardViews.jsx | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/selenium-test.js b/frontend/selenium-test.js index d371f4d5..42b6b801 100644 --- a/frontend/selenium-test.js +++ b/frontend/selenium-test.js @@ -23,6 +23,7 @@ console.log('Starting Selenium script for testing pages...'); try { await driver.manage().window().setRect({ width: 1600, height: 1200 }); + await driver.manage().window().maximize() await driver.manage().setTimeouts({ implicit: 15000 }); const frontendURL = process.argv[2]; @@ -85,8 +86,10 @@ console.log('Starting Selenium script for testing pages...'); await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000); await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD); - await driver.wait(until.elementLocated(By.css('#loginButton')), 10000); - await driver.findElement(By.css('#loginButton')).click(); + const loginButton = await driver.findElement(By.css('#loginButton')); + await driver.executeScript("arguments[0].scrollIntoView(true);", loginButton); + await loginButton.click(); + // Ensure signup success by checking URL change diff --git a/frontend/src/views/DashboardViews.jsx b/frontend/src/views/DashboardViews.jsx index 4bf532a6..7905a21a 100644 --- a/frontend/src/views/DashboardViews.jsx +++ b/frontend/src/views/DashboardViews.jsx @@ -8,7 +8,7 @@ import { useNavigate, Link, useParams } from "react-router-dom"; import { ToastContainer, toast } from "react-toastify" import Draggable from "react-draggable"; -// import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; +import DashboardBarchart, { LoadStats } from '../components/DashboardBarchart.jsx'; import { Autocomplete, From d17713ce4e1d826618bd1fe057d840719d62059e Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Wed, 12 Mar 2025 00:53:26 +0530 Subject: [PATCH 066/200] Add notification job when workflow fails --- .github/workflows/quick-testing.yml | 34 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 6af52777..cba689bd 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -162,21 +162,21 @@ jobs: echo "Health check failed after $MAX_RETRIES attempts." exit 1 - # notify: - # needs: build - # if: failure() - # runs-on: ubuntu-latest - # steps: - # - name: Send Twilio Alert - # run: | - # WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - # MESSAGE="🚨 Shuffle Workflow Failed! - # Repository: ${{ github.repository }} - # Branch: ${{ github.ref_name }} - # Workflow URL: $WORKFLOW_URL" + notify: + needs: build + if: failure() + runs-on: ubuntu-latest + steps: + - name: Send Twilio Alert + run: | + WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + MESSAGE="🚨 Shuffle Workflow Failed! + Repository: ${{ github.repository }} + Branch: ${{ github.ref_name }} + Workflow URL: $WORKFLOW_URL" - # curl -s -X POST https://api.twilio.com/2010-04-01/Accounts/${{ secrets.TWILIO_ACCOUNT_SID }}/Messages.json \ - # --data-urlencode "To=${{ secrets.TWILIO_TO_NUMBER }}" \ - # --data-urlencode "From=${{ secrets.TWILIO_FROM_NUMBER }}" \ - # --data-urlencode "Body=$MESSAGE" \ - # -u "${{ secrets.TWILIO_ACCOUNT_SID }}:${{ secrets.TWILIO_AUTH_TOKEN }}" > /dev/null + curl -s -X POST https://api.twilio.com/2010-04-01/Accounts/${{ secrets.TWILIO_ACCOUNT_SID }}/Messages.json \ + --data-urlencode "To=${{ secrets.TWILIO_TO_NUMBER }}" \ + --data-urlencode "From=${{ secrets.TWILIO_FROM_NUMBER }}" \ + --data-urlencode "Body=$MESSAGE" \ + -u "${{ secrets.TWILIO_ACCOUNT_SID }}:${{ secrets.TWILIO_AUTH_TOKEN }}" > /dev/null From aa49670dbbb72202c66a9e26de3386ee4bbd43bf Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Wed, 12 Mar 2025 10:28:52 +0530 Subject: [PATCH 067/200] Don't send health api request multiple time if fails --- .github/workflows/quick-testing.yml | 57 ++++++++++++----------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index cba689bd..cf35967a 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -122,45 +122,34 @@ jobs: exit 1 fi - MAX_RETRIES=5 # Total attempts (1 initial + 4 retries) - RETRY_INTERVAL=10 # Retry every 10 seconds after first failure + echo "Waiting 1 minute before sending the health API request..." + sleep 60 - echo "Waiting 1 minute before the first health check..." - sleep 60 # Initial wait before first attempt + echo "Checking health API..." + HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \ + -H "Authorization: Bearer $API_KEY") - for ((i=1; i<=$MAX_RETRIES; i++)); do - echo "Attempt $i/$MAX_RETRIES: Checking health API..." - - HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \ - -H "Authorization: Bearer $API_KEY") + echo "Health API Response: $HEALTH_RESPONSE" - echo "Health API Response: $HEALTH_RESPONSE" + WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create') + WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run') + WORKFLOWS_RUN_FINISHED=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_finished') + WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status') + WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete') - WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create') - WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run') - WORKFLOWS_RUN_FINISHED=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_finished') - WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status') - WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete') + echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE" + echo "WORKFLOWS_RUN: $WORKFLOWS_RUN" + echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED" + echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS" + echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE" - echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE" - echo "WORKFLOWS_RUN: $WORKFLOWS_RUN" - echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED" - echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS" - echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE" - - if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then - echo "Health endpoint check was successful." - exit 0 - fi - - if [ "$i" -lt "$MAX_RETRIES" ]; then - echo "Health check failed. Retrying in $RETRY_INTERVAL seconds..." - sleep $RETRY_INTERVAL - fi - done - - echo "Health check failed after $MAX_RETRIES attempts." - exit 1 + if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then + echo "Health endpoint check was successful." + exit 0 + else + echo "Health check failed." + exit 1 + fi notify: needs: build From 99a4cab4249c196abb16a47e9a56c6fb8c0f2920 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 14 Mar 2025 11:16:19 +0100 Subject: [PATCH 068/200] Minor worker MEMCACHED fixes --- functions/onprem/orborus/orborus.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 2f6af9fe..ffb21af4 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -466,8 +466,9 @@ func deployServiceWorkers(image string) { } ip := "shuffle-cache" - - os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip)) + if len(os.Getenv("SHUFFLE_MEMCACHED")) == 0 { + os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip)) + } defaultNetworkAttach := false if containerId != "" { @@ -608,7 +609,7 @@ func deployServiceWorkers(image string) { fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")), fmt.Sprintf("DEBUG_MEMORY=%s", os.Getenv("DEBUG_MEMORY")), fmt.Sprintf("SHUFFLE_APP_SDK_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")), - fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%d", os.Getenv("SHUFFLE_MAX_SWARM_NODES")), + fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%s", os.Getenv("SHUFFLE_MAX_SWARM_NODES")), fmt.Sprintf("SHUFFLE_BASE_IMAGE_NAME=%s", os.Getenv("SHUFFLE_BASE_IMAGE_NAME")), fmt.Sprintf("SHUFFLE_APP_REQUEST_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_REQUEST_TIMEOUT")), }, From 7710deabd3a4f425b492f066ba4e52f87bc5186e Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Mon, 17 Mar 2025 17:42:20 +0530 Subject: [PATCH 069/200] fix for auto memcached setup --- functions/onprem/orborus/orborus.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 2f6af9fe..8cf902f6 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -456,18 +456,20 @@ func deployServiceWorkers(image string) { } } - isMemcachedRunning, err := checkMemcached(ctx, dockercli) - if err != nil { - log.Printf("[ERROR] Failed checking memcached: %s", err) - } - if isMemcachedRunning == false { - log.Printf("[ERROR] Memcached is not running. Will try to deploy it.") - deployMemcached(dockercli) - } + // Only do this if no memcached is set manually + if os.Getenv("SHUFFLE_MEMCACHED") == "" { + isMemcachedRunning, err := checkMemcached(ctx, dockercli) + if err != nil { + log.Printf("[ERROR] Failed checking memcached: %s", err) + } + if isMemcachedRunning == false { + log.Printf("[ERROR] Memcached is not running. Will try to deploy it.") + deployMemcached(dockercli) + } - ip := "shuffle-cache" - - os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip)) + ip := "shuffle-cache" + os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip)) + } defaultNetworkAttach := false if containerId != "" { From 02801ecd7885ccad6a83b65c680cdc61580aad64 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Mon, 17 Mar 2025 18:10:06 +0530 Subject: [PATCH 070/200] fixed force health url --- backend/go-app/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 42b58256..bbdf54c7 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4385,7 +4385,7 @@ func runInitEs(ctx context.Context) { request := http.Request{} // Add the "force=true" query to the fake request - request.URL, err = url.Parse("/api/v1/health/stats?force=true") + request.URL, err = url.Parse("/api/v1/health?force=true") if err != nil { log.Printf("[ERROR] Failed to parse test url for healthstats: %s", err) } From b6512897e8d921ebeec273875410b54e20b516a9 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Mon, 17 Mar 2025 18:12:23 +0530 Subject: [PATCH 071/200] Revert "Multiple diff fixes for multi tenant workflows" This reverts commit f0c2dfa3d22f12eaa8479a079619bae6b7a367a7. --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 2 - backend/go-app/walkoff.go | 8 +- frontend/src/components/Billing.jsx | 81 ++++++-------- frontend/src/components/EditWorkflow.jsx | 15 +-- frontend/src/components/LicencePopup.jsx | 70 +++--------- frontend/src/components/Navbar.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 134 ++++++----------------- frontend/src/views/LoginPage.jsx | 20 ++-- frontend/src/views/Workflows.jsx | 5 +- frontend/src/views/Workflows2.jsx | 14 +-- 11 files changed, 106 insertions(+), 247 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index cf2eb962..8af42425 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.12 + github.com/shuffle/shuffle-shared v0.8.7 golang.org/x/crypto v0.32.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.68.1 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 99179e7f..f240b4e4 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -356,8 +356,6 @@ github.com/shuffle/shuffle-shared v0.8.5 h1:c98AMOzIrDXmgQrKyf1HJ9wZwAwB02LPRvf9 github.com/shuffle/shuffle-shared v0.8.5/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/shuffle/shuffle-shared v0.8.6 h1:chnGJRKsO1gR5Vt/8hp5SQg1yP8/DmiK8sRhWVaTgEg= github.com/shuffle/shuffle-shared v0.8.6/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.8.12 h1:fg6jGAuevOGLgR7kpoZDJh//tzFjrEqj/bpxRFzxUTw= -github.com/shuffle/shuffle-shared v0.8.12/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index dfdccefe..037d6d32 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1032,14 +1032,10 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { log.Printf("[INFO] Should have deleted workflow %s (%s)", workflow.Name, fileId) - shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.Id)) + cacheKey := fmt.Sprintf("%s_workflows", user.Id) + shuffle.DeleteCache(ctx, cacheKey) shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.ActiveOrg.Id)) - shuffle.DeleteCache(ctx, fmt.Sprintf("%s_%s", user.Username, fileId)) log.Printf("[DEBUG] Cleared workflow cache for %s (%s)", user.Username, user.Id) - shuffle.DeleteCache(ctx, fmt.Sprintf("workflow_%s_childworkflows", workflow.ID)) - if len(workflow.ParentWorkflowId) > 0 { - shuffle.DeleteCache(ctx, fmt.Sprintf("workflow_%s_childworkflows", workflow.ParentWorkflowId)) - } resp.WriteHeader(200) resp.Write([]byte(`{"success": true}`)) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 39e15706..577d249c 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -77,8 +77,6 @@ const Billing = memo((props) => { const [currentIndex, setCurrentIndex] = useState(0); const [deleteAlertIndex, setDeleteAlertIndex] = useState(-1); const [deleteAlertVerification, setDeleteAlertVerification] = useState(false); - const [isScale, setIsScale] = useState(false); - useEffect(() => { if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) { const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100; @@ -178,10 +176,11 @@ const Billing = memo((props) => { padding: 20, // maxWidth: 400, width: 340, - height: 'auto', + height: 480, // width: "100%", - backgroundColor: "#1e1e1e", - borderRadius: 10, + backgroundColor: theme.palette.backgroundColor, + borderRadius: theme.palette?.borderRadius * 2, + border: "1px solid rgba(255,255,255,0.3)", marginRight: 10, marginTop: 15, } @@ -362,7 +361,7 @@ const Billing = memo((props) => { var showSupport = false if (subscription.name.includes("default")) { top_text = "Custom Contract" - // newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" + newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" showSupport = true } @@ -378,17 +377,16 @@ const Billing = memo((props) => { if (subscription.name.includes("Scale")) { top_text = "Scale access" - setIsScale(true) } if (highlight === true) { // Add an "Upgrade now" button - // newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" + newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" } - // if (hovered) { - // newPaperstyle.backgroundColor = "#2b2b2b" - // } + if (hovered) { + newPaperstyle.backgroundColor = "#2b2b2b" + } const handleClickOpen = () => { setOpenChangeEmailBox(true); @@ -558,7 +556,6 @@ const Billing = memo((props) => {
-
{top_text === "Base Cloud Access" && userdata.has_card_available === true ? { { }} @@ -2016,33 +2013,7 @@ const Billing = memo((props) => { : null} -
-
- {isCloud && - selectedOrganization.subscriptions !== undefined && - selectedOrganization.subscriptions !== null && - selectedOrganization.subscriptions.length > 0 && - !isChildOrg ? - selectedOrganization.subscriptions - .reverse() - .map((sub, index) => { - return ( - - ) - }) - : null} - -
+
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null : { isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} - isScale={isScale} {...props} /> : !isCloud ? @@ -2091,7 +2061,6 @@ const Billing = memo((props) => { isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} - isScale={isScale} /> {/* { /> */} : null} -
-
+ {isCloud && + selectedOrganization.subscriptions !== undefined && + selectedOrganization.subscriptions !== null && + selectedOrganization.subscriptions.length > 0 && + !isChildOrg ? + selectedOrganization.subscriptions + .reverse() + .map((sub, index) => { + return ( + + ) + }) + : null} {/* { */}
+ {/*isCloud && selectedOrganization.partner_info !== undefined && selectedOrganization.partner_info.reseller === true ? ( diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 48574126..b67bde93 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -577,20 +577,12 @@ const EditWorkflow = (props) => { Multi-Tenancy, Backups & Security + - Control mechanisms for multi-tenancy, backups, and security. + Multi-Tenant Workflows. Make one workflow, and keep a separate, synced copy in all your tenants. Control distributed auth, runtime locations, files, datastore keys etc. (contact support@shuffler.io if you want a demo. Please try it!) - - Multi-Tenant Workflows - - - - Make one workflow, and keep a separate, synced copy in your other tenants. Control distributed auth, runtime locations, files, datastore keys etc. Can only distribute from parent org to child org. Need help trying it?
Contact us for a demo - - - {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? - + {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ? userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ? @@ -608,7 +600,6 @@ const EditWorkflow = (props) => { multiple style={{ marginTop: 10, }} value={innerWorkflow.suborg_distribution === undefined || innerWorkflow.suborg_distribution === null ? ["none"] : innerWorkflow.suborg_distribution} - disabled={workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0} onChange={(e) => { var newvalue = e.target.value if (newvalue.length > 1 && newvalue[0] === "none") { diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index 9492ae99..f52ffa45 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -46,7 +46,7 @@ import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" import Billing from "./Billing.jsx"; const LicencePopup = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isScale, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; //const alert = useAlert(); let navigate = useNavigate(); const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); @@ -165,25 +165,11 @@ const LicencePopup = (props) => { ] } - if (userdata?.app_execution_limit >= 300000) { - subscription.name = "Enterprise" - subscription.currency_text = "$" - subscription.price = typecost_single - subscription.limit = userdata?.app_execution_limit - subscription.interval = "app run / month" - subscription.features = [ - "Includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. ", - "Multi-Tenancy and Region-Selection", - "And all other features from /pricing", - ] - } - - var newPaperstyle = JSON.parse(JSON.stringify(paperStyle)) if (subscription.name === "Enterprise" && subscription.active === true) { - top_text = "Enterprise Plan" + top_text = "Current Plan" - // newPaperstyle.border = "1px solid #f85a3e" + newPaperstyle.border = "1px solid #f85a3e" } var showSupport = false @@ -269,7 +255,7 @@ const LicencePopup = (props) => { style={{ borderRadius: theme.palette?.borderRadius, }} placement="bottom" > -
+
setHovered(true)} @@ -357,9 +343,9 @@ const LicencePopup = (props) => {
- {subscription.active === true && !isScale && } +
- {top_text === "Base Cloud Access" && userdata.has_card_available === true && !isScale ? + {top_text === "Base Cloud Access" && userdata.has_card_available === true ? { "While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month." : isCloud ? - userdata?.app_execution_limit && userdata?.app_execution_limit >= 300000 ? - "You have subscribed to the Enterprise plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." : - `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` - + `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` : `You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.` } @@ -716,33 +699,8 @@ const LicencePopup = (props) => { setCalculatedCost("$960") setSelectedValue(8) } else { - if (userdata && userdata?.app_execution_limit) { - if (userdata.app_execution_limit >= 300000 && userdata.app_execution_limit < 400000) { - setSelectedValue(400) - setCalculatedCost("$1280") - }else if (userdata?.app_execution_limit >= 400000 && userdata?.app_execution_limit < 500000) { - setSelectedValue(500) - setCalculatedCost("$1600") - } else if (userdata?.app_execution_limit >= 500000 && userdata?.app_execution_limit < 600000) { - setSelectedValue(600) - setCalculatedCost("$1920") - } else if (userdata?.app_execution_limit >= 600000 && userdata?.app_execution_limit < 700000) { - setSelectedValue(700) - setCalculatedCost("$2240") - } else if (userdata?.app_execution_limit >= 700000 && userdata?.app_execution_limit < 800000) { - setSelectedValue(800) - setCalculatedCost("$2560") - } else if (userdata?.app_execution_limit >= 800000 && userdata?.app_execution_limit < 900000) { - setSelectedValue(900) - setCalculatedCost("$2880") - }else { - setCalculatedCost("$960") - setSelectedValue(300) - } - }else { - setCalculatedCost("$960") - setSelectedValue(300) - } + setCalculatedCost("$960") + setSelectedValue(300) } }, [shuffleVariant]) @@ -1019,7 +977,7 @@ const LicencePopup = (props) => { : null} - {/* {isCloud && + {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ? @@ -1040,7 +998,7 @@ const LicencePopup = (props) => { /> ) }) - : null} */} + : null} @@ -1048,12 +1006,12 @@ const LicencePopup = (props) => {
- { !isScale && } + color="primary">Recommended
{shuffleVariant === 1 ? "Scale" : "Enterprise"} diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index c3cca3f8..ebee1db5 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -47,7 +47,7 @@ const menuData = { title: "Shuffle", description: "The most versatile automation engine with focus on security.", - icon: "images/icons/shuffleLogo.svg", + icon: "images/logos/orange_logo.svg", path: "/docs/about", gaData: { category: "navbar", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 374ce85b..79a9d98a 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -575,17 +575,11 @@ const AngularWorkflow = (defaultprops) => { }, [editWorkflowModalOpen]) useEffect(() => { - // Check if selectedTriggerIndex is a number >= 0 - if (isNaN(selectedTriggerIndex) || selectedTriggerIndex < 0) { - return - } - - if (selectedTrigger?.trigger_type === "SUBFLOW" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 1) { + if(selectedTrigger?.trigger_type === "SUBFLOW"){ setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[1].value || "") - } else if (selectedTrigger?.trigger_type === "USERINPUT" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 0) { + } else if(selectedTrigger?.trigger_type === "USERINPUT"){ setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[0].value || "Do you want to continue the workflow? Start parameters: $exec") } - }, [selectedTriggerIndex, selectedTrigger, workflow]) useEffect(() => { @@ -954,15 +948,11 @@ const AngularWorkflow = (defaultprops) => { } useEffect(() => { - if (workflow?.actions?.length == 1) { - if (workflow?.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") { + if (workflow.actions?.length == 1) { + if (workflow.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") { setWorkflowAsCode(true); } } - - if (workflow?.suborg_distribution !== undefined && workflow?.suborg_distribution !== null && workflow?.suborg_distribution.length > 0) { - getChildWorkflows(workflow.id) - } }, [workflow]); // Event for making sure app is correct @@ -1505,8 +1495,6 @@ const AngularWorkflow = (defaultprops) => { if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { headers["Org-Id"] = workflow.org_id } - - setWorkflows([]) fetch(globalUrl + "/api/v1/workflows?subflow=true", { method: "GET", @@ -1524,7 +1512,7 @@ const AngularWorkflow = (defaultprops) => { if (responseJson !== undefined) { // Sets up subflow trigger with the right info - if (isNaN(trigger_index) === false && trigger_index > -1) { + if (trigger_index > -1) { var baseSubflow = {} const trigger = workflow.triggers[trigger_index]; @@ -2506,19 +2494,15 @@ const AngularWorkflow = (defaultprops) => { if (cy !== undefined && cy !== null) { // scale: 0.3, // bg: "#27292d", - if (cy?.png !== undefined && cy?.png !== null) { - try { - const cyImageData = cy.png({ - output: "base64uri", - maxWidth: 480, - maxHeight: 270, - }) + if (cy.png !== undefined && cy.png !== null) { + const cyImageData = cy.png({ + output: "base64uri", + maxWidth: 480, + maxHeight: 270, + }) - if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { - useworkflow.image = cyImageData - } - } catch (e) { - console.log("Failed to get image data: ", e) + if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { + useworkflow.image = cyImageData } } } @@ -3930,10 +3914,6 @@ const AngularWorkflow = (defaultprops) => { } const findWorkflowDiff = (parentWorkflow, childWorkflow) => { - // Ensures new memory is used - parentWorkflow = JSON.parse(JSON.stringify(parentWorkflow)) - childWorkflow = JSON.parse(JSON.stringify(childWorkflow)) - var diff = { "different": false, "environment": false, @@ -3951,13 +3931,9 @@ const AngularWorkflow = (defaultprops) => { return diff } + var parentEnvironment = "" var childEnvironment = "" - - if (parentWorkflow.triggers !== undefined && parentWorkflow.triggers !== null && parentWorkflow.triggers.length > 0) { - parentWorkflow.actions = parentWorkflow.actions.concat(parentWorkflow.triggers) - } - for (var parentKey in parentWorkflow.actions) { const parentAction = parentWorkflow.actions[parentKey] if (parentAction.environment !== undefined && parentAction.environment !== null && parentAction.environment !== "") { @@ -3969,19 +3945,6 @@ const AngularWorkflow = (defaultprops) => { } var found = false - if (childWorkflow.triggers !== undefined && childWorkflow.triggers !== null && childWorkflow.triggers.length > 0) { - for (var triggerKey in childWorkflow.triggers) { - if (childWorkflow.triggers[triggerKey].replacement_for_trigger !== parentAction.id) { - continue - } - - // Rewrapping the ID just in case - childWorkflow.triggers[triggerKey].id = childWorkflow.triggers[triggerKey].replacement_for_trigger - childWorkflow.actions.push(childWorkflow.triggers[triggerKey]) - break - } - } - for (var childKey in childWorkflow.actions) { const childAction = childWorkflow.actions[childKey] if (childAction.environment !== undefined && childAction.environment !== null && childAction.environment !== "") { @@ -4028,10 +3991,6 @@ const AngularWorkflow = (defaultprops) => { continue } - var parentworkflow = "" - var parentstartnode = "" - var childworkflow = "" - var childstartnode = "" for (var parentParamIndex in parentAction.parameters) { const parentParam = parentAction.parameters[parentParamIndex] for (var childParamIndex in childAction.parameters) { @@ -4040,38 +3999,20 @@ const AngularWorkflow = (defaultprops) => { continue } - if (childParam.name === "workflow" || childParam.name === "subflow") { - parentworkflow = parentParam.value - childworkflow = childParam.value - continue - } - - if (childParam.name == "startnode") { - parentstartnode = parentParam.value - childstartnode = childParam.value - continue - } - if (childParam.value !== parentParam.value) { actionDiff.parameters.push(childParam.name) } } } - - if (parentworkflow !== childworkflow && parentstartnode !== childstartnode) { - actionDiff.parameters.push("subflow") - } } if (actionDiff.parameters.length > 0) { actionDiff.params = true } - /* if (!found) { actionDiff.new = true } - */ if (actionDiff !== undefined && actionDiff !== null && Object.keys(actionDiff).length > 1) { actionDiff.label = parentAction.label.replaceAll("_", " ") @@ -6364,15 +6305,11 @@ const AngularWorkflow = (defaultprops) => { if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0 && originalWorkflow.org_id !== undefined && originalWorkflow.org_id !== null && originalWorkflow.org_id.length > 0 && workflow.org_id === originalWorkflow.org_id) { // Allows a parent workflow to control the schedule } else if (data.replacement_for_trigger !== undefined && data.replacement_for_trigger !== null && data.replacement_for_trigger.length > 0) { - - // No custom control on cloud - if (isCloud) { - toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", { - autoClose: 30000, - }) - event.target.unselect() - return - } + toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", { + autoClose: 30000, + }) + event.target.unselect() + return } } else if (data.app_name === "Webhook" && trigger_index >= 0) { @@ -6948,7 +6885,7 @@ const AngularWorkflow = (defaultprops) => { ) if (targetnode !== -1) { if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") { - //console.log("User Input or Shuffle Workflow") + console.log("User Input or Shuffle Workflow") } else { toast("Can't have triggers as target of branch") event.target.remove() @@ -9280,15 +9217,11 @@ const AngularWorkflow = (defaultprops) => { } insertedNodes = insertedNodes.concat(newedges); - setWorkflow(inputworkflow) + setWorkflow(inputworkflow); // Reset view for cytoscape if (cy !== undefined && cy !== null) { - try { - cy.add(insertedNodes) - } catch (error) { - console.log("Error adding nodes to cytoscape (6): ", error) - } + cy.add(insertedNodes) try { cy.fit(null, 250) @@ -9367,6 +9300,12 @@ const AngularWorkflow = (defaultprops) => { } } + //if (selectedNode.data("id") === selectedAction.id) { + // setSelectedApp({}); + // setSelectedAction({}); + //setSelectedTrigger({}); + //setSelectedTriggerIndex({}); + //} const parsedSelection = cy.$(":selected"); if (selectedNode.data().decorator === true && selectedNode.data("type") !== "COMMENT") { toast("This node can't be deleted."); @@ -9566,6 +9505,7 @@ const AngularWorkflow = (defaultprops) => { if (firstrequest) { setFirstrequest(false) getWorkflow(props.match.params.key, {}) + getChildWorkflows(props.match.params.key) getRevisionHistory(props.match.params.key) loadTriggers() getApps() @@ -14951,15 +14891,12 @@ const AngularWorkflow = (defaultprops) => { }} >
- Select a workflow to run + Select a workflow to execute
- {workflow.triggers[selectedTriggerIndex].parameters[0].value .length === 0 ? null : workflow.triggers[selectedTriggerIndex] - .parameters[0].value === props.match.params.key ? - null - : ( + .parameters[0].value === props.match.params.key ? null : (
{ {workflows === undefined || workflows === null || - workflows.length === 0 ? - - : ( + workflows.length === 0 ? null : ( + { value={workflow.org_id} disabled={savingState !== 0 || suborgWorkflows?.length === 0 || allTriggers === undefined} onChange={(e) => { - if (cy !== undefined && cy !== null) { - cy.nodes().unselect() - } - if (lastSaved === false && originalWorkflow.id === workflow.id) { setSuborgWorkflows([]) diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 97c8b5cf..385a2d8e 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -427,14 +427,14 @@ const LoginPage = props => { window.location.pathname = "/workflows" } - const checkAdmin = () => { - const url = globalUrl + "/api/v1/checkusers"; - fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) + const checkAdmin = () => { + const url = globalUrl + "/api/v1/checkusers"; + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { @@ -459,7 +459,7 @@ const LoginPage = props => { navigate("/adminsetup") }, 2500) }) - } + }; const onSubmit = (e) => { @@ -952,7 +952,7 @@ const LoginPage = props => {
return ( -
+
{loadedCheck}
) diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 100eed13..1eee8317 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1946,10 +1946,7 @@ const Workflows = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); - - if (bulk !== true) { - toast(`Failed deleting workflow ${id}. Do you have access?`); - } + toast("Failed deleting workflow. Do you have access?"); } else { if (bulk !== true) { toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`) diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index f1e18120..0b079407 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -2043,10 +2043,7 @@ const Workflows2 = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); - - if (bulk !== true) { - toast("Failed deleting workflow. Do you have access?"); - } + toast("Failed deleting workflow. Do you have access?"); } else { if (bulk !== true) { toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`) @@ -2824,7 +2821,7 @@ const Workflows2 = (props) => { {(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") && type !== "public" ? -
+
{ : null} {(data?.validation?.validation_ran === true && data?.validation?.valid === false && data?.validation?.errors?.length > 0 ) ? - -
+ +
{
- : null} + : null}
@@ -3238,7 +3235,6 @@ const Workflows2 = (props) => {
} - Date: Mon, 17 Mar 2025 18:24:56 +0530 Subject: [PATCH 072/200] fix: Reverted to f0c2dfa --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 2 + backend/go-app/main.go | 2 +- backend/go-app/walkoff.go | 8 +- frontend/src/components/Billing.jsx | 81 ++++++++------ frontend/src/components/EditWorkflow.jsx | 15 ++- frontend/src/components/LicencePopup.jsx | 70 +++++++++--- frontend/src/components/Navbar.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 134 +++++++++++++++++------ frontend/src/views/LoginPage.jsx | 20 ++-- frontend/src/views/Workflows.jsx | 5 +- frontend/src/views/Workflows2.jsx | 14 ++- functions/onprem/orborus/orborus.go | 24 ++-- 13 files changed, 259 insertions(+), 120 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 8af42425..cf2eb962 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.7 + github.com/shuffle/shuffle-shared v0.8.12 golang.org/x/crypto v0.32.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.68.1 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index f240b4e4..99179e7f 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -356,6 +356,8 @@ github.com/shuffle/shuffle-shared v0.8.5 h1:c98AMOzIrDXmgQrKyf1HJ9wZwAwB02LPRvf9 github.com/shuffle/shuffle-shared v0.8.5/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/shuffle/shuffle-shared v0.8.6 h1:chnGJRKsO1gR5Vt/8hp5SQg1yP8/DmiK8sRhWVaTgEg= github.com/shuffle/shuffle-shared v0.8.6/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= +github.com/shuffle/shuffle-shared v0.8.12 h1:fg6jGAuevOGLgR7kpoZDJh//tzFjrEqj/bpxRFzxUTw= +github.com/shuffle/shuffle-shared v0.8.12/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index bbdf54c7..42b58256 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4385,7 +4385,7 @@ func runInitEs(ctx context.Context) { request := http.Request{} // Add the "force=true" query to the fake request - request.URL, err = url.Parse("/api/v1/health?force=true") + request.URL, err = url.Parse("/api/v1/health/stats?force=true") if err != nil { log.Printf("[ERROR] Failed to parse test url for healthstats: %s", err) } diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 037d6d32..dfdccefe 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1032,10 +1032,14 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { log.Printf("[INFO] Should have deleted workflow %s (%s)", workflow.Name, fileId) - cacheKey := fmt.Sprintf("%s_workflows", user.Id) - shuffle.DeleteCache(ctx, cacheKey) + shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.Id)) shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.ActiveOrg.Id)) + shuffle.DeleteCache(ctx, fmt.Sprintf("%s_%s", user.Username, fileId)) log.Printf("[DEBUG] Cleared workflow cache for %s (%s)", user.Username, user.Id) + shuffle.DeleteCache(ctx, fmt.Sprintf("workflow_%s_childworkflows", workflow.ID)) + if len(workflow.ParentWorkflowId) > 0 { + shuffle.DeleteCache(ctx, fmt.Sprintf("workflow_%s_childworkflows", workflow.ParentWorkflowId)) + } resp.WriteHeader(200) resp.Write([]byte(`{"success": true}`)) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 577d249c..39e15706 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -77,6 +77,8 @@ const Billing = memo((props) => { const [currentIndex, setCurrentIndex] = useState(0); const [deleteAlertIndex, setDeleteAlertIndex] = useState(-1); const [deleteAlertVerification, setDeleteAlertVerification] = useState(false); + const [isScale, setIsScale] = useState(false); + useEffect(() => { if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) { const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100; @@ -176,11 +178,10 @@ const Billing = memo((props) => { padding: 20, // maxWidth: 400, width: 340, - height: 480, + height: 'auto', // width: "100%", - backgroundColor: theme.palette.backgroundColor, - borderRadius: theme.palette?.borderRadius * 2, - border: "1px solid rgba(255,255,255,0.3)", + backgroundColor: "#1e1e1e", + borderRadius: 10, marginRight: 10, marginTop: 15, } @@ -361,7 +362,7 @@ const Billing = memo((props) => { var showSupport = false if (subscription.name.includes("default")) { top_text = "Custom Contract" - newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" + // newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" showSupport = true } @@ -377,16 +378,17 @@ const Billing = memo((props) => { if (subscription.name.includes("Scale")) { top_text = "Scale access" + setIsScale(true) } if (highlight === true) { // Add an "Upgrade now" button - newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" + // newPaperstyle.border = "1px solid rgba(255,255,255,0.3)" } - if (hovered) { - newPaperstyle.backgroundColor = "#2b2b2b" - } + // if (hovered) { + // newPaperstyle.backgroundColor = "#2b2b2b" + // } const handleClickOpen = () => { setOpenChangeEmailBox(true); @@ -556,6 +558,7 @@ const Billing = memo((props) => {
+
{top_text === "Base Cloud Access" && userdata.has_card_available === true ? { { }} @@ -2013,7 +2016,33 @@ const Billing = memo((props) => { : null} -
+
+
+ {isCloud && + selectedOrganization.subscriptions !== undefined && + selectedOrganization.subscriptions !== null && + selectedOrganization.subscriptions.length > 0 && + !isChildOrg ? + selectedOrganization.subscriptions + .reverse() + .map((sub, index) => { + return ( + + ) + }) + : null} + +
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null : { isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} + isScale={isScale} {...props} /> : !isCloud ? @@ -2061,6 +2091,7 @@ const Billing = memo((props) => { isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} + isScale={isScale} /> {/* { /> */} : null} +
+
- {isCloud && - selectedOrganization.subscriptions !== undefined && - selectedOrganization.subscriptions !== null && - selectedOrganization.subscriptions.length > 0 && - !isChildOrg ? - selectedOrganization.subscriptions - .reverse() - .map((sub, index) => { - return ( - - ) - }) - : null} {/* { */}
- {/*isCloud && selectedOrganization.partner_info !== undefined && selectedOrganization.partner_info.reseller === true ? ( diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index b67bde93..48574126 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -577,12 +577,20 @@ const EditWorkflow = (props) => { Multi-Tenancy, Backups & Security - - Multi-Tenant Workflows. Make one workflow, and keep a separate, synced copy in all your tenants. Control distributed auth, runtime locations, files, datastore keys etc. (contact support@shuffler.io if you want a demo. Please try it!) + Control mechanisms for multi-tenancy, backups, and security. - {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? + + Multi-Tenant Workflows + + + + Make one workflow, and keep a separate, synced copy in your other tenants. Control distributed auth, runtime locations, files, datastore keys etc. Can only distribute from parent org to child org. Need help trying it?
Contact us for a demo + + + {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? + userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ? userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ? @@ -600,6 +608,7 @@ const EditWorkflow = (props) => { multiple style={{ marginTop: 10, }} value={innerWorkflow.suborg_distribution === undefined || innerWorkflow.suborg_distribution === null ? ["none"] : innerWorkflow.suborg_distribution} + disabled={workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0} onChange={(e) => { var newvalue = e.target.value if (newvalue.length > 1 && newvalue[0] === "none") { diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index f52ffa45..9492ae99 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -46,7 +46,7 @@ import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" import Billing from "./Billing.jsx"; const LicencePopup = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isScale, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; //const alert = useAlert(); let navigate = useNavigate(); const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); @@ -165,11 +165,25 @@ const LicencePopup = (props) => { ] } + if (userdata?.app_execution_limit >= 300000) { + subscription.name = "Enterprise" + subscription.currency_text = "$" + subscription.price = typecost_single + subscription.limit = userdata?.app_execution_limit + subscription.interval = "app run / month" + subscription.features = [ + "Includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. ", + "Multi-Tenancy and Region-Selection", + "And all other features from /pricing", + ] + } + + var newPaperstyle = JSON.parse(JSON.stringify(paperStyle)) if (subscription.name === "Enterprise" && subscription.active === true) { - top_text = "Current Plan" + top_text = "Enterprise Plan" - newPaperstyle.border = "1px solid #f85a3e" + // newPaperstyle.border = "1px solid #f85a3e" } var showSupport = false @@ -255,7 +269,7 @@ const LicencePopup = (props) => { style={{ borderRadius: theme.palette?.borderRadius, }} placement="bottom" > -
+
setHovered(true)} @@ -343,9 +357,9 @@ const LicencePopup = (props) => {
- + {subscription.active === true && !isScale && }
- {top_text === "Base Cloud Access" && userdata.has_card_available === true ? + {top_text === "Base Cloud Access" && userdata.has_card_available === true && !isScale ? { "While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month." : isCloud ? - `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` + userdata?.app_execution_limit && userdata?.app_execution_limit >= 300000 ? + "You have subscribed to the Enterprise plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." : + `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` + : `You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.` } @@ -699,8 +716,33 @@ const LicencePopup = (props) => { setCalculatedCost("$960") setSelectedValue(8) } else { - setCalculatedCost("$960") - setSelectedValue(300) + if (userdata && userdata?.app_execution_limit) { + if (userdata.app_execution_limit >= 300000 && userdata.app_execution_limit < 400000) { + setSelectedValue(400) + setCalculatedCost("$1280") + }else if (userdata?.app_execution_limit >= 400000 && userdata?.app_execution_limit < 500000) { + setSelectedValue(500) + setCalculatedCost("$1600") + } else if (userdata?.app_execution_limit >= 500000 && userdata?.app_execution_limit < 600000) { + setSelectedValue(600) + setCalculatedCost("$1920") + } else if (userdata?.app_execution_limit >= 600000 && userdata?.app_execution_limit < 700000) { + setSelectedValue(700) + setCalculatedCost("$2240") + } else if (userdata?.app_execution_limit >= 700000 && userdata?.app_execution_limit < 800000) { + setSelectedValue(800) + setCalculatedCost("$2560") + } else if (userdata?.app_execution_limit >= 800000 && userdata?.app_execution_limit < 900000) { + setSelectedValue(900) + setCalculatedCost("$2880") + }else { + setCalculatedCost("$960") + setSelectedValue(300) + } + }else { + setCalculatedCost("$960") + setSelectedValue(300) + } } }, [shuffleVariant]) @@ -977,7 +1019,7 @@ const LicencePopup = (props) => { : null} - {isCloud && + {/* {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ? @@ -998,7 +1040,7 @@ const LicencePopup = (props) => { /> ) }) - : null} + : null} */} @@ -1006,12 +1048,12 @@ const LicencePopup = (props) => {
- + color="primary">Recommended }
{shuffleVariant === 1 ? "Scale" : "Enterprise"} diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index ebee1db5..c3cca3f8 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -47,7 +47,7 @@ const menuData = { title: "Shuffle", description: "The most versatile automation engine with focus on security.", - icon: "images/logos/orange_logo.svg", + icon: "images/icons/shuffleLogo.svg", path: "/docs/about", gaData: { category: "navbar", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 79a9d98a..374ce85b 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -575,11 +575,17 @@ const AngularWorkflow = (defaultprops) => { }, [editWorkflowModalOpen]) useEffect(() => { - if(selectedTrigger?.trigger_type === "SUBFLOW"){ + // Check if selectedTriggerIndex is a number >= 0 + if (isNaN(selectedTriggerIndex) || selectedTriggerIndex < 0) { + return + } + + if (selectedTrigger?.trigger_type === "SUBFLOW" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 1) { setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[1].value || "") - } else if(selectedTrigger?.trigger_type === "USERINPUT"){ + } else if (selectedTrigger?.trigger_type === "USERINPUT" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 0) { setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[0].value || "Do you want to continue the workflow? Start parameters: $exec") } + }, [selectedTriggerIndex, selectedTrigger, workflow]) useEffect(() => { @@ -948,11 +954,15 @@ const AngularWorkflow = (defaultprops) => { } useEffect(() => { - if (workflow.actions?.length == 1) { - if (workflow.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") { + if (workflow?.actions?.length == 1) { + if (workflow?.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") { setWorkflowAsCode(true); } } + + if (workflow?.suborg_distribution !== undefined && workflow?.suborg_distribution !== null && workflow?.suborg_distribution.length > 0) { + getChildWorkflows(workflow.id) + } }, [workflow]); // Event for making sure app is correct @@ -1495,6 +1505,8 @@ const AngularWorkflow = (defaultprops) => { if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { headers["Org-Id"] = workflow.org_id } + + setWorkflows([]) fetch(globalUrl + "/api/v1/workflows?subflow=true", { method: "GET", @@ -1512,7 +1524,7 @@ const AngularWorkflow = (defaultprops) => { if (responseJson !== undefined) { // Sets up subflow trigger with the right info - if (trigger_index > -1) { + if (isNaN(trigger_index) === false && trigger_index > -1) { var baseSubflow = {} const trigger = workflow.triggers[trigger_index]; @@ -2494,15 +2506,19 @@ const AngularWorkflow = (defaultprops) => { if (cy !== undefined && cy !== null) { // scale: 0.3, // bg: "#27292d", - if (cy.png !== undefined && cy.png !== null) { - const cyImageData = cy.png({ - output: "base64uri", - maxWidth: 480, - maxHeight: 270, - }) + if (cy?.png !== undefined && cy?.png !== null) { + try { + const cyImageData = cy.png({ + output: "base64uri", + maxWidth: 480, + maxHeight: 270, + }) - if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { - useworkflow.image = cyImageData + if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { + useworkflow.image = cyImageData + } + } catch (e) { + console.log("Failed to get image data: ", e) } } } @@ -3914,6 +3930,10 @@ const AngularWorkflow = (defaultprops) => { } const findWorkflowDiff = (parentWorkflow, childWorkflow) => { + // Ensures new memory is used + parentWorkflow = JSON.parse(JSON.stringify(parentWorkflow)) + childWorkflow = JSON.parse(JSON.stringify(childWorkflow)) + var diff = { "different": false, "environment": false, @@ -3931,9 +3951,13 @@ const AngularWorkflow = (defaultprops) => { return diff } - var parentEnvironment = "" var childEnvironment = "" + + if (parentWorkflow.triggers !== undefined && parentWorkflow.triggers !== null && parentWorkflow.triggers.length > 0) { + parentWorkflow.actions = parentWorkflow.actions.concat(parentWorkflow.triggers) + } + for (var parentKey in parentWorkflow.actions) { const parentAction = parentWorkflow.actions[parentKey] if (parentAction.environment !== undefined && parentAction.environment !== null && parentAction.environment !== "") { @@ -3945,6 +3969,19 @@ const AngularWorkflow = (defaultprops) => { } var found = false + if (childWorkflow.triggers !== undefined && childWorkflow.triggers !== null && childWorkflow.triggers.length > 0) { + for (var triggerKey in childWorkflow.triggers) { + if (childWorkflow.triggers[triggerKey].replacement_for_trigger !== parentAction.id) { + continue + } + + // Rewrapping the ID just in case + childWorkflow.triggers[triggerKey].id = childWorkflow.triggers[triggerKey].replacement_for_trigger + childWorkflow.actions.push(childWorkflow.triggers[triggerKey]) + break + } + } + for (var childKey in childWorkflow.actions) { const childAction = childWorkflow.actions[childKey] if (childAction.environment !== undefined && childAction.environment !== null && childAction.environment !== "") { @@ -3991,6 +4028,10 @@ const AngularWorkflow = (defaultprops) => { continue } + var parentworkflow = "" + var parentstartnode = "" + var childworkflow = "" + var childstartnode = "" for (var parentParamIndex in parentAction.parameters) { const parentParam = parentAction.parameters[parentParamIndex] for (var childParamIndex in childAction.parameters) { @@ -3999,20 +4040,38 @@ const AngularWorkflow = (defaultprops) => { continue } + if (childParam.name === "workflow" || childParam.name === "subflow") { + parentworkflow = parentParam.value + childworkflow = childParam.value + continue + } + + if (childParam.name == "startnode") { + parentstartnode = parentParam.value + childstartnode = childParam.value + continue + } + if (childParam.value !== parentParam.value) { actionDiff.parameters.push(childParam.name) } } } + + if (parentworkflow !== childworkflow && parentstartnode !== childstartnode) { + actionDiff.parameters.push("subflow") + } } if (actionDiff.parameters.length > 0) { actionDiff.params = true } + /* if (!found) { actionDiff.new = true } + */ if (actionDiff !== undefined && actionDiff !== null && Object.keys(actionDiff).length > 1) { actionDiff.label = parentAction.label.replaceAll("_", " ") @@ -6305,11 +6364,15 @@ const AngularWorkflow = (defaultprops) => { if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0 && originalWorkflow.org_id !== undefined && originalWorkflow.org_id !== null && originalWorkflow.org_id.length > 0 && workflow.org_id === originalWorkflow.org_id) { // Allows a parent workflow to control the schedule } else if (data.replacement_for_trigger !== undefined && data.replacement_for_trigger !== null && data.replacement_for_trigger.length > 0) { - toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", { - autoClose: 30000, - }) - event.target.unselect() - return + + // No custom control on cloud + if (isCloud) { + toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", { + autoClose: 30000, + }) + event.target.unselect() + return + } } } else if (data.app_name === "Webhook" && trigger_index >= 0) { @@ -6885,7 +6948,7 @@ const AngularWorkflow = (defaultprops) => { ) if (targetnode !== -1) { if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") { - console.log("User Input or Shuffle Workflow") + //console.log("User Input or Shuffle Workflow") } else { toast("Can't have triggers as target of branch") event.target.remove() @@ -9217,11 +9280,15 @@ const AngularWorkflow = (defaultprops) => { } insertedNodes = insertedNodes.concat(newedges); - setWorkflow(inputworkflow); + setWorkflow(inputworkflow) // Reset view for cytoscape if (cy !== undefined && cy !== null) { - cy.add(insertedNodes) + try { + cy.add(insertedNodes) + } catch (error) { + console.log("Error adding nodes to cytoscape (6): ", error) + } try { cy.fit(null, 250) @@ -9300,12 +9367,6 @@ const AngularWorkflow = (defaultprops) => { } } - //if (selectedNode.data("id") === selectedAction.id) { - // setSelectedApp({}); - // setSelectedAction({}); - //setSelectedTrigger({}); - //setSelectedTriggerIndex({}); - //} const parsedSelection = cy.$(":selected"); if (selectedNode.data().decorator === true && selectedNode.data("type") !== "COMMENT") { toast("This node can't be deleted."); @@ -9505,7 +9566,6 @@ const AngularWorkflow = (defaultprops) => { if (firstrequest) { setFirstrequest(false) getWorkflow(props.match.params.key, {}) - getChildWorkflows(props.match.params.key) getRevisionHistory(props.match.params.key) loadTriggers() getApps() @@ -14891,12 +14951,15 @@ const AngularWorkflow = (defaultprops) => { }} >
- Select a workflow to execute + Select a workflow to run
+ {workflow.triggers[selectedTriggerIndex].parameters[0].value .length === 0 ? null : workflow.triggers[selectedTriggerIndex] - .parameters[0].value === props.match.params.key ? null : ( + .parameters[0].value === props.match.params.key ? + null + : (
{ {workflows === undefined || workflows === null || - workflows.length === 0 ? null : ( - + workflows.length === 0 ? + + : ( { value={workflow.org_id} disabled={savingState !== 0 || suborgWorkflows?.length === 0 || allTriggers === undefined} onChange={(e) => { + if (cy !== undefined && cy !== null) { + cy.nodes().unselect() + } + if (lastSaved === false && originalWorkflow.id === workflow.id) { setSuborgWorkflows([]) diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 385a2d8e..97c8b5cf 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -427,14 +427,14 @@ const LoginPage = props => { window.location.pathname = "/workflows" } - const checkAdmin = () => { - const url = globalUrl + "/api/v1/checkusers"; - fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) + const checkAdmin = () => { + const url = globalUrl + "/api/v1/checkusers"; + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { @@ -459,7 +459,7 @@ const LoginPage = props => { navigate("/adminsetup") }, 2500) }) - }; + } const onSubmit = (e) => { @@ -952,7 +952,7 @@ const LoginPage = props => {
return ( -
+
{loadedCheck}
) diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 1eee8317..100eed13 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1946,7 +1946,10 @@ const Workflows = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); - toast("Failed deleting workflow. Do you have access?"); + + if (bulk !== true) { + toast(`Failed deleting workflow ${id}. Do you have access?`); + } } else { if (bulk !== true) { toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`) diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 0b079407..f1e18120 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -2043,7 +2043,10 @@ const Workflows2 = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); - toast("Failed deleting workflow. Do you have access?"); + + if (bulk !== true) { + toast("Failed deleting workflow. Do you have access?"); + } } else { if (bulk !== true) { toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`) @@ -2821,7 +2824,7 @@ const Workflows2 = (props) => { {(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") && type !== "public" ? -
+
{ : null} {(data?.validation?.validation_ran === true && data?.validation?.valid === false && data?.validation?.errors?.length > 0 ) ? - -
+ +
{
- : null} +
@@ -3235,6 +3238,7 @@ const Workflows2 = (props) => {
} + Date: Mon, 17 Mar 2025 18:29:23 +0530 Subject: [PATCH 073/200] fixed health check onprem --- backend/go-app/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 42b58256..bbdf54c7 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4385,7 +4385,7 @@ func runInitEs(ctx context.Context) { request := http.Request{} // Add the "force=true" query to the fake request - request.URL, err = url.Parse("/api/v1/health/stats?force=true") + request.URL, err = url.Parse("/api/v1/health?force=true") if err != nil { log.Printf("[ERROR] Failed to parse test url for healthstats: %s", err) } From 5395ad40ec12b2ea0792534cff4b770696ab0c6e Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 18 Mar 2025 00:53:26 +0100 Subject: [PATCH 074/200] Minor sync --- backend/go-app/docker.go | 7 -- frontend/src/App.jsx | 27 +++++++- frontend/src/views/Docs.jsx | 71 +++++++++++++++------ functions/onprem/orborus/go.mod | 5 +- functions/onprem/orborus/go.sum | 10 +-- functions/onprem/orborus/orborus.go | 99 +++++++++++++++-------------- functions/onprem/worker/worker.go | 2 - 7 files changed, 133 insertions(+), 88 deletions(-) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 57d4aaf8..11bbe14d 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -984,11 +984,4 @@ func activateWorkflowAppDocker(resp http.ResponseWriter, request *http.Request) log.Printf("[INFO] User %s (%s) is activating %s. Public: %t, Shared: %t", user.Username, user.Id, app.Name, app.Public, app.Sharing) buildSwaggerApp(resp, []byte(openApiApp.Body), user, true) - - //app.Active = true - //app.Generated = true - //app, err := shuffle.SetApp(ctx, app) - - //resp.WriteHeader(200) - //resp.Write([]byte(`{"success": true}`)) } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 419df99d..392db7f6 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -634,6 +634,32 @@ const App = (message, props) => { } /> } /> + + } + /> + + } + /> { exact path="/docs" element={ - //navigate(`/docs/about`) { } return( - {props.alt} +
+ {props.alt} +
) } @@ -278,8 +280,6 @@ export const CodeHandler = (props) => { const Docs = (defaultprops) => { const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded, userdata } = defaultprops; - console.log("\n\nSELECTED DOC\n", selectedDoc, "\n\n") - let navigate = useNavigate(); const location = useLocation(); // Quickfix for react router 5 -> 6 @@ -321,14 +321,22 @@ const Docs = (defaultprops) => { useEffect(() => { fetchDocList(); setSidebarOpen(false); - }, [location]); - useEffect(() => { - //if (params["key"] === undefined) { - // navigate("/docs/about") - // return - //} - }, []) + if (props.match.params === undefined || props.match.params === null) { + return + } + + const propkey = props.match.params.key + if (propkey === undefined || propkey === null) { + return + } + + if (location.pathname.includes("/docs/")) { + if (propkey === "cookie_policy" || propkey === "compliance" || propkey === "privacy_policy" || propkey === "terms_of_service") { + navigate(`/legal/${propkey}`) + } + } + }, [location]); const handleClick = (event) => { setAnchorEl(event.currentTarget); @@ -761,7 +769,13 @@ const Docs = (defaultprops) => { } const fetchDocList = (resetCache = false) => { - const url = isArticlePage ? `${globalUrl}/api/v1/articles?resetCache=${resetCache}` : `${globalUrl}/api/v1/docs`; + var url = `${globalUrl}/api/v1/docs` + if (location.pathname.includes("/legal")) { + url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}` + } else if (location.pathname.includes("/articles")) { + url = `${globalUrl}/api/v1/docs?folder=articles&resetCache=${resetCache}` + } + fetch(url, { method: "GET", headers: { @@ -782,8 +796,18 @@ const Docs = (defaultprops) => { .catch((error) => { }); }; - const fetchDocs = (docId) => { - const url = isArticlePage ? `${globalUrl}/api/v1/articles/${docId}` : `${globalUrl}/api/v1/docs/${docId}`; + const fetchDoc = (docId) => { + if (docId === undefined) { + return + } + + var url = `${globalUrl}/api/v1/docs/${docId}` + if (location.pathname.includes("/legal")) { + url = `${globalUrl}/api/v1/docs/${docId}?folder=legal` + } else if (location.pathname.includes("/articles")) { + url = `${globalUrl}/api/v1/docs/${docId}?folder=articles` + } + fetch(url, { method: "GET", headers: { @@ -902,7 +926,7 @@ const Docs = (defaultprops) => { }); } else { console.log("DOCID: ", props.match.params.key) - fetchDocs(props.match.params.key) + fetchDoc(props.match.params.key) } } } @@ -911,7 +935,7 @@ const Docs = (defaultprops) => { // Handles search-based changes that origin from outside this file if (serverside !== true && window.location.href !== baseUrl) { setBaseUrl(window.location.href); - fetchDocs(props.match.params.key); + fetchDoc(props.match.params.key); } const markdownStyle = { @@ -1106,7 +1130,14 @@ const Docs = (defaultprops) => { if (item === undefined) { return null; } - const path = "/docs/" + item; + + var path = "/docs/" + item; + if (location.pathname.includes("/legal")) { + path = "/legal/" + item + } else if (location.pathname.includes("/articles")) { + path = "/articles/" + item + } + const newname = item.charAt(0).toUpperCase() + item.substring(1).split("_").join(" ").split("-").join(" "); diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 7909c2ee..bb0fcb9e 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -1,9 +1,10 @@ module orborus -go 1.22.0 +go 1.22.7 + toolchain go1.23.6 -//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared require ( github.com/docker/docker v27.5.0+incompatible diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 2bb0c986..7fc88f31 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -123,12 +123,10 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= - github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= - github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -305,10 +303,11 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shuffle/shuffle-shared v0.6.99 h1:sPGmZo+8JMgUH9Q2O659za2w4sF/NXiWFCrSPm1nTAU= github.com/shuffle/shuffle-shared v0.6.99/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= +github.com/shuffle/shuffle-shared v0.8.7 h1:+UdRx7b/KUy/E92ODNr87UvCI2Dxfnv0samQWuggJu4= +github.com/shuffle/shuffle-shared v0.8.7/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= - github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -369,7 +368,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= - golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -427,7 +425,6 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= - golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -437,7 +434,6 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= - golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -474,14 +470,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= - golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= - golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index ffb21af4..dbc79434 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -456,6 +456,7 @@ func deployServiceWorkers(image string) { } } + /* isMemcachedRunning, err := checkMemcached(ctx, dockercli) if err != nil { log.Printf("[ERROR] Failed checking memcached: %s", err) @@ -469,6 +470,7 @@ func deployServiceWorkers(image string) { if len(os.Getenv("SHUFFLE_MEMCACHED")) == 0 { os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip)) } + */ defaultNetworkAttach := false if containerId != "" { @@ -767,10 +769,8 @@ func handleBackendImageDownload(ctx context.Context, images string) error { // Remove the image handled := []string{} - log.Printf("[DEBUG] Should remove existing image (s): %s. Waiting 30 seconds to ensure backend has the latest images built and ready to distribute.", images) - removeOptions := image.RemoveOptions{} - - time.Sleep(time.Duration(30) * time.Second) + log.Printf("[DEBUG] Removing existing image (s): %s. Waiting 30 seconds before starting to ensure backend has the latest images built and ready to distribute.", images) + //time.Sleep(time.Duration(30) * time.Second) newImages := []string{} for _, image := range strings.Split(images, ",") { @@ -786,23 +786,12 @@ func handleBackendImageDownload(ctx context.Context, images string) error { newImages = append(newImages, image) - // There is no real point in actual removal. This may however be a good idea, as Worker will force download the new one anyway - resp, err := dockercli.ImageRemove(ctx, image, removeOptions) + log.Printf("[DEBUG] Downloading image: %s", image) + err := shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image) if err != nil { - log.Printf("[ERROR] Failed removing image: %s. Resp: %#v", err, resp) - - // Goroutining images that don't already exist, as they are most likely not the correct one - go shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image) + log.Printf("[ERROR] Failed downloading image: %s", err) } else { - log.Printf("[DEBUG] Removed image: %s", image) - - err = shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image) - if err != nil { - log.Printf("[ERROR] Failed downloading image: %s", err) - } else { - log.Printf("[DEBUG] Downloaded image: %s", image) - //break - } + log.Printf("[DEBUG] Downloaded image: %s", image) } } @@ -821,20 +810,18 @@ func handleBackendImageDownload(ctx context.Context, images string) error { ) if err != nil { - log.Printf("[ERROR] Failed finding containers: %s", err) + log.Printf("[ERROR] Failed finding services: %s", err) } else { - log.Printf("[DEBUG] Found %d services", len(services)) - + found := false for _, service := range services { - - log.Printf("Imagename: %s", service.Spec.TaskTemplate.ContainerSpec.Image) + //log.Printf("Service image: %s", service.Spec.TaskTemplate.ContainerSpec.Image) for _, image := range newImages { if !strings.Contains(service.Spec.TaskTemplate.ContainerSpec.Image, image) { continue } - log.Printf("[DEBUG] Found service for image %#v: %#v", service.Spec.Annotations.Name) + log.Printf("[DEBUG] Found service for image: %#v", service.Spec.Annotations.Name) // Update the service to run with the new image //docker service update --image username/imagename:latest servicename --force @@ -854,9 +841,20 @@ func handleBackendImageDownload(ctx context.Context, images string) error { if !strings.Contains(fmt.Sprintf("%s", resp), "error") { break + } else { + found = true + log.Printf("[ERROR] Failed updating service %s with the new image %s: %s. Resp: %#v", service.Spec.Annotations.Name, image, err, resp) } } } + + if found { + break + } + } + + if !found { + log.Printf("[DEBUG] Failed to find service to update for service %s", newImages) } } @@ -1480,29 +1478,34 @@ func initializeImages() { } // check whether they are the same first - images := []string{ - fmt.Sprintf("frikky/shuffle:app_sdk"), - fmt.Sprintf("shuffle/shuffle:app_sdk"), - fmt.Sprintf("%s/%s/shuffle-app_sdk:%s", baseimageregistry, baseimagename, appSdkVersion), - newWorker, - } - pullOptions := image.PullOptions{} - for _, image := range images { - if isKubernetes == "true" { - log.Printf("[DEBUG] Skipping image pull of '%s' because Kubernetes does it in realtime instead", image) - } else { - log.Printf("[DEBUG] Pulling image %s", image) - reader, err := dockercli.ImagePull(ctx, image, pullOptions) - if err != nil { - log.Printf("[ERROR] Failed getting image %s: %s", image, err) - - continue - } - - io.Copy(os.Stdout, reader) - log.Printf("[DEBUG] Successfully downloaded and built %s", image) + if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") != "true" { + images := []string{ + fmt.Sprintf("frikky/shuffle:app_sdk"), + fmt.Sprintf("shuffle/shuffle:app_sdk"), + fmt.Sprintf("%s/%s/shuffle-app_sdk:%s", baseimageregistry, baseimagename, appSdkVersion), + newWorker, } + + pullOptions := image.PullOptions{} + for _, image := range images { + if isKubernetes == "true" { + log.Printf("[DEBUG] Skipping image pull of '%s' because Kubernetes does it in realtime instead", image) + } else { + log.Printf("[DEBUG] Pulling image %s", image) + reader, err := dockercli.ImagePull(ctx, image, pullOptions) + if err != nil { + log.Printf("[ERROR] Failed getting image %s: %s", image, err) + + continue + } + + io.Copy(os.Stdout, reader) + log.Printf("[DEBUG] Successfully downloaded and built %s", image) + } + } + } else { + log.Printf("[DEBUG] Skipping image download as SHUFFLE_AUTO_IMAGE_DOWNLOAD is set to true") } } @@ -2246,7 +2249,7 @@ func main() { toBeRemoved.Data = append(toBeRemoved.Data, incRequest) } else if incRequest.Type == "DOCKER_IMAGE_DOWNLOAD" { - log.Printf("[INFO] Should delete -> download new images: %#v", incRequest.ExecutionArgument) + log.Printf("[INFO] Re-downloading new image(s): %#v", incRequest.ExecutionArgument) if len(incRequest.ExecutionArgument) > 0 { // FIXME: Wait X seconds before running this as the image build may not be done yet. This is shitty, but may be ok to do in Orborus. Easy fix for the future: Just let it run through jobs 5-10 times before actually picking it up @@ -3463,7 +3466,7 @@ func sendPipelineHealthStatus() (shuffle.LakeConfig, error) { } else { tenzirDisabled = true - log.Printf("[ERROR] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err) + log.Printf("[WARNING] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err) } return pipelinePayload, err diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index dca31904..943dcd2f 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -106,9 +106,7 @@ var window = shuffle.NewTimeWindow(10 * time.Second) // Images to be autodeployed in the latest version of Shuffle. var autoDeploy = map[string]string{ "http:1.4.0": "frikky/shuffle:http_1.4.0", - "http:1.3.0": "frikky/shuffle:http_1.3.0", "shuffle-tools:1.2.0": "frikky/shuffle:shuffle-tools_1.2.0", - "shuffle-subflow:1.0.0": "frikky/shuffle:shuffle-subflow_1.0.0", "shuffle-subflow:1.1.0": "frikky/shuffle:shuffle-subflow_1.1.0", // "shuffle-tools-fork:1.0.0": "frikky/shuffle:shuffle-tools-fork_1.0.0", } From 13cac512dfd0c38c02cecc6eff6c09a49957f99f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 00:19:57 +0000 Subject: [PATCH 075/200] build(deps): bump github.com/containerd/containerd in /backend/go-app Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.26 to 1.6.38. - [Release notes](https://github.com/containerd/containerd/releases) - [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md) - [Commits](https://github.com/containerd/containerd/compare/v1.6.26...v1.6.38) --- updated-dependencies: - dependency-name: github.com/containerd/containerd dependency-type: indirect ... Signed-off-by: dependabot[bot] --- backend/go-app/go.mod | 4 +- backend/go-app/go.sum | 102 ++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 65 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index cf2eb962..2645d538 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -37,6 +37,7 @@ require ( cloud.google.com/go/iam v1.1.7 // indirect cloud.google.com/go/scheduler v1.10.6 // indirect dario.cat/mergo v1.0.0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -47,7 +48,6 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/containerd/containerd v1.6.26 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -114,8 +114,10 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v0.11.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.34.0 // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 99179e7f..cf5605ea 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -15,8 +15,6 @@ cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKF cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -37,8 +35,6 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 h1:V8krnnfGj4pV65YLUm3C0/8bl7V5Nry2Pwvy3ru/wLc= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -48,8 +44,6 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.9.10 h1:TxXGNmcbQxBKVWvjvTocNb6jrPyeHlk5EiDhhgHgggs= -github.com/Microsoft/hcsshim v0.9.10/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= @@ -61,6 +55,7 @@ github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= @@ -90,6 +85,7 @@ github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -97,14 +93,17 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/containerd v1.6.26 h1:VVfrE6ZpyisvB1fzoY8Vkiq4sy+i5oF4uk7zu03RaHs= -github.com/containerd/containerd v1.6.26/go.mod h1:I4TRdsdoo5MlKob5khDJS2EPT1l1oMNaE2MBm6FrwxM= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -112,12 +111,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g= -github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -132,6 +127,8 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -157,11 +154,12 @@ github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -196,6 +194,7 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -212,6 +211,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -297,8 +297,6 @@ github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -316,8 +314,6 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= @@ -335,9 +331,10 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6V7LB6EL8= github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= @@ -350,12 +347,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdR github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.4 h1:R/A62IzHJXnhVNG1PqFXN1YtRgdiiSIL5q4YJEef5P8= -github.com/shuffle/shuffle-shared v0.8.4/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.8.5 h1:c98AMOzIrDXmgQrKyf1HJ9wZwAwB02LPRvf9+yy05OQ= -github.com/shuffle/shuffle-shared v0.8.5/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.8.6 h1:chnGJRKsO1gR5Vt/8hp5SQg1yP8/DmiK8sRhWVaTgEg= -github.com/shuffle/shuffle-shared v0.8.6/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/shuffle/shuffle-shared v0.8.12 h1:fg6jGAuevOGLgR7kpoZDJh//tzFjrEqj/bpxRFzxUTw= github.com/shuffle/shuffle-shared v0.8.12/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -365,7 +356,6 @@ github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0 github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= @@ -401,30 +391,25 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0 h1:Ydage/P0fRrSPpZeCVxzjqGcI6iVmG2xb43+IR8cjqM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= @@ -438,8 +423,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -488,6 +471,7 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -496,8 +480,6 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -505,8 +487,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -534,10 +514,12 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -545,16 +527,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -584,7 +562,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -640,15 +617,12 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c h1:kaI7oewGK5YnVwj+Y+EJBO/YN1ht8iTL9XkFHtVZLsc= -google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -659,9 +633,10 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -675,8 +650,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -696,6 +670,7 @@ gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQb gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -703,9 +678,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= -gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 41f623dd396e8de1fe8c3f439da1b005bd02bc51 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:14:33 +0100 Subject: [PATCH 076/200] run as user 1001 by default Signed-off-by: Pascal Sthamer <10992664+P4sca1@users.noreply.github.com> --- functions/kubernetes/charts/shuffle/README.md | 13 +++++++------ .../charts/shuffle/values.schema.json | 12 ++++++------ functions/kubernetes/charts/shuffle/values.yaml | 17 +++++++++-------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/functions/kubernetes/charts/shuffle/README.md b/functions/kubernetes/charts/shuffle/README.md index 495d2690..2ae2926f 100644 --- a/functions/kubernetes/charts/shuffle/README.md +++ b/functions/kubernetes/charts/shuffle/README.md @@ -184,8 +184,8 @@ SHUFFLE_ENCRYPTION_MODIFIER: "MyShuffleEncryptionModifier" | `backend.podSecurityContext.fsGroup` | Set fsGroup in backend pods' Security Context | `1001` | | `backend.containerSecurityContext.enabled` | Enabled backend container' Security Context | `true` | | `backend.containerSecurityContext.seLinuxOptions` | Set SELinux options in backend container | `{}` | -| `backend.containerSecurityContext.runAsUser` | Set runAsUser in backend container' Security Context | `1000` | -| `backend.containerSecurityContext.runAsGroup` | Set runAsGroup in backend container' Security Context | `1000` | +| `backend.containerSecurityContext.runAsUser` | Set runAsUser in backend container' Security Context | `1001` | +| `backend.containerSecurityContext.runAsGroup` | Set runAsGroup in backend container' Security Context | `1001` | | `backend.containerSecurityContext.runAsNonRoot` | Set runAsNonRoot in backend container' Security Context | `true` | | `backend.containerSecurityContext.readOnlyRootFilesystem` | Set readOnlyRootFilesystem in backend container' Security Context | `true` | | `backend.containerSecurityContext.privileged` | Set privileged in backend container' Security Context | `false` | @@ -302,8 +302,8 @@ SHUFFLE_ENCRYPTION_MODIFIER: "MyShuffleEncryptionModifier" | `frontend.podSecurityContext.fsGroup` | Set fsGroup in frontend pods' Security Context | `1001` | | `frontend.containerSecurityContext.enabled` | Enabled frontend container' Security Context | `false` | | `frontend.containerSecurityContext.seLinuxOptions` | Set SELinux options in frontend container | `{}` | -| `frontend.containerSecurityContext.runAsUser` | Set runAsUser in frontend container' Security Context | `101` | -| `frontend.containerSecurityContext.runAsGroup` | Set runAsGroup in frontend container' Security Context | `101` | +| `frontend.containerSecurityContext.runAsUser` | Set runAsUser in frontend container' Security Context | `1001` | +| `frontend.containerSecurityContext.runAsGroup` | Set runAsGroup in frontend container' Security Context | `1001` | | `frontend.containerSecurityContext.runAsNonRoot` | Set runAsNonRoot in frontend container' Security Context | `true` | | `frontend.containerSecurityContext.readOnlyRootFilesystem` | Set readOnlyRootFilesystem in frontend container' Security Context | `true` | | `frontend.containerSecurityContext.privileged` | Set privileged in frontend container' Security Context | `false` | @@ -409,8 +409,8 @@ SHUFFLE_ENCRYPTION_MODIFIER: "MyShuffleEncryptionModifier" | `orborus.podSecurityContext.fsGroup` | Set fsGroup in orborus pods' Security Context | `1001` | | `orborus.containerSecurityContext.enabled` | Enabled orborus container' Security Context | `true` | | `orborus.containerSecurityContext.seLinuxOptions` | Set SELinux options in orborus container | `{}` | -| `orborus.containerSecurityContext.runAsUser` | Set runAsUser in orborus container' Security Context | `101` | -| `orborus.containerSecurityContext.runAsGroup` | Set runAsGroup in orborus container' Security Context | `101` | +| `orborus.containerSecurityContext.runAsUser` | Set runAsUser in orborus container' Security Context | `1001` | +| `orborus.containerSecurityContext.runAsGroup` | Set runAsGroup in orborus container' Security Context | `1001` | | `orborus.containerSecurityContext.runAsNonRoot` | Set runAsNonRoot in orborus container' Security Context | `true` | | `orborus.containerSecurityContext.readOnlyRootFilesystem` | Set readOnlyRootFilesystem in orborus container' Security Context | `true` | | `orborus.containerSecurityContext.privileged` | Set privileged in orborus container' Security Context | `false` | @@ -605,3 +605,4 @@ SHUFFLE_ENCRYPTION_MODIFIER: "MyShuffleEncryptionModifier" ### Other Parameters + diff --git a/functions/kubernetes/charts/shuffle/values.schema.json b/functions/kubernetes/charts/shuffle/values.schema.json index 7a85b9fa..609e37df 100644 --- a/functions/kubernetes/charts/shuffle/values.schema.json +++ b/functions/kubernetes/charts/shuffle/values.schema.json @@ -367,12 +367,12 @@ "runAsUser": { "type": "number", "description": "Set runAsUser in backend container' Security Context", - "default": 1000 + "default": 1001 }, "runAsGroup": { "type": "number", "description": "Set runAsGroup in backend container' Security Context", - "default": 1000 + "default": 1001 }, "runAsNonRoot": { "type": "boolean", @@ -1056,12 +1056,12 @@ "runAsUser": { "type": "number", "description": "Set runAsUser in frontend container' Security Context", - "default": 101 + "default": 1001 }, "runAsGroup": { "type": "number", "description": "Set runAsGroup in frontend container' Security Context", - "default": 101 + "default": 1001 }, "runAsNonRoot": { "type": "boolean", @@ -1675,12 +1675,12 @@ "runAsUser": { "type": "number", "description": "Set runAsUser in orborus container' Security Context", - "default": 101 + "default": 1001 }, "runAsGroup": { "type": "number", "description": "Set runAsGroup in orborus container' Security Context", - "default": 101 + "default": 1001 }, "runAsNonRoot": { "type": "boolean", diff --git a/functions/kubernetes/charts/shuffle/values.yaml b/functions/kubernetes/charts/shuffle/values.yaml index 35492f94..221b0bb5 100644 --- a/functions/kubernetes/charts/shuffle/values.yaml +++ b/functions/kubernetes/charts/shuffle/values.yaml @@ -235,8 +235,8 @@ backend: containerSecurityContext: enabled: true seLinuxOptions: {} - runAsUser: 1000 - runAsGroup: 1000 + runAsUser: 1001 + runAsGroup: 1001 runAsNonRoot: true readOnlyRootFilesystem: true privileged: false @@ -683,11 +683,12 @@ frontend: ## @param frontend.podSecurityContext.fsGroup Set fsGroup in frontend pods' Security Context ## podSecurityContext: - enabled: false + enabled: false # The default shuffle frontend image does not support running as non-root, because /etc/nginx/nginx.conf is written on startup fsGroupChangePolicy: Always sysctls: [] supplementalGroups: [] fsGroup: 1001 + ## Configure Container Security Context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container ## @param frontend.containerSecurityContext.enabled Enabled frontend container' Security Context @@ -702,10 +703,10 @@ frontend: ## @param frontend.containerSecurityContext.seccompProfile.type Set seccomp profile in frontend container ## containerSecurityContext: - enabled: false + enabled: false # The default shuffle frontend image does not support running as non-root, because /etc/nginx/nginx.conf is written on startup seLinuxOptions: {} - runAsUser: 101 - runAsGroup: 101 + runAsUser: 1001 + runAsGroup: 1001 runAsNonRoot: true readOnlyRootFilesystem: true privileged: false @@ -1107,8 +1108,8 @@ orborus: containerSecurityContext: enabled: true seLinuxOptions: {} - runAsUser: 101 - runAsGroup: 101 + runAsUser: 1001 + runAsGroup: 1001 runAsNonRoot: true readOnlyRootFilesystem: true privileged: false From 10811b4624ee4a194f86ee690f55bb78dd9891c9 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 18 Mar 2025 13:29:36 +0100 Subject: [PATCH 077/200] Fixed multiple navigation issues --- frontend/src/components/Billing.jsx | 11 +- frontend/src/components/LeftSideBar.jsx | 8 +- frontend/src/components/Navbar.jsx | 38 +++++-- frontend/src/components/SearchData.jsx | 124 ++++++++++++++--------- frontend/src/context/ContextApi.jsx | 3 + frontend/src/views/Docs.jsx | 128 +++++++++++++++++++++++- functions/onprem/orborus/orborus.go | 31 ++++-- 7 files changed, 272 insertions(+), 71 deletions(-) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 39e15706..3be59424 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -860,6 +860,7 @@ const Billing = memo((props) => { "Add Card Details" } + {userdata.has_card_available === true ?
} /> {/* @@ -897,28 +921,38 @@ const SearchData = props => { const modalView = (
- - - - - + { + !isDocSearchModal ? ( + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + ) : ( + + + + + + ) + }
) @@ -932,7 +966,7 @@ const SearchData = props => { {modalView} - {gettingStartData} + {!isDocSearchModal && gettingStartData}
) } diff --git a/frontend/src/context/ContextApi.jsx b/frontend/src/context/ContextApi.jsx index 4e82ba1e..00b8b764 100644 --- a/frontend/src/context/ContextApi.jsx +++ b/frontend/src/context/ContextApi.jsx @@ -8,6 +8,7 @@ export const AppContext = (props) => { // Left side bar global states const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); + const [isDocSearchModalOpen, setIsDocSearchModalOpen] = useState(false); const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true) const [windowWidth, setWindowWidth] = useState(serverside === true ? 100 : window.innerWidth); @@ -37,6 +38,8 @@ export const AppContext = (props) => { return ( { const Docs = (defaultprops) => { const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded, userdata } = defaultprops; + const { searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen, setIsDocSearchModalOpen, leftSideBarOpenByClick } = useContext(Context); + let navigate = useNavigate(); const location = useLocation(); // Quickfix for react router 5 -> 6 @@ -316,6 +326,7 @@ const Docs = (defaultprops) => { const [activeSubItem, setActiveSubItem] = useState(false); const headingElementsRef = useRef({}) var isArticlePage = window.location.pathname.includes("/articles/") || window.location.pathname === "/articles" ? true : false; + const searchFieldRef = useRef(null); useEffect(() => { @@ -338,6 +349,13 @@ const Docs = (defaultprops) => { } }, [location]); + useEffect(() => { + return () => { + setIsDocSearchModalOpen(false); + setSearchBarModalOpen(false); + }; + }, []); + const handleClick = (event) => { setAnchorEl(event.currentTarget); } @@ -467,6 +485,71 @@ const Docs = (defaultprops) => { return headings; }; + const modalView = ( + { + setSearchBarModalOpen(false); + if (searchFieldRef.current) { + searchFieldRef.current.blur(); + } + }} + PaperProps={{ + style: { + color: "white", + minWidth: 750, + minHeight: "180px", + maxHeight: "85vh", + borderRadius: 16, + border: "1px solid var(--Container-Stroke, #494949)", + background: "var(--Container, #000000)", + boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)", + position: "fixed", + top: "70px", + left: "50%", + transform: "translateX(-50%)", + }, + }} + sx={{ + zIndex: 50005, + '& .MuiBackdrop-root': { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + }, + }} + > + + + Search for Documentation + + setSearchBarModalOpen(false)} + sx={{ + color: 'white', + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)' + } + }} + > + + + + + + + + + + + ); + // extract TOC from actual markdown const tocvalue = (markdown) => { @@ -1117,9 +1200,50 @@ const Docs = (defaultprops) => {
+ {modalView} + { + !isArticlePage && ( + { + setIsDocSearchModalOpen(true) + setSearchBarModalOpen(true) + }} + inputRef={searchFieldRef} + InputProps={{ + endAdornment: ( + + + + ), + style: { + backgroundColor: "#212121", + borderRadius: 4, + color: "white", + }, + }} + style={{ + marginTop: 10, + marginLeft: 10, + borderRadius: 4, + width: !isLoggedIn ? "14%" : !leftSideBarOpenByClick ? "12%" : "11%", + maxWidth: "285px", + position: "fixed", + zIndex: 1100, + }} + inputProps={{ + readOnly: true, + }} + /> + ) + } Date: Tue, 18 Mar 2025 19:03:13 +0100 Subject: [PATCH 078/200] Fixed some minor tracker issues --- frontend/src/components/Billing.jsx | 4 +- frontend/src/components/ssoTab.jsx | 115 +++++++++++++------ frontend/src/views/LoginPage.jsx | 171 ++++++++++++++++++---------- 3 files changed, 192 insertions(+), 98 deletions(-) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 3be59424..44e36334 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -1210,8 +1210,8 @@ const Billing = memo((props) => { } const url = userdata.licensed === true - ? "https://book.stripe.com/6oEeY23fw2T84M06oq" - : "https://book.stripe.com/00g6rw3fw9hwguI289" + ? "https://buy.stripe.com/aEU3fk17o0L092g5kt" + : "https://buy.stripe.com/aEU3fk17o0L092g5kt" window.open(url, "_blank") }} > diff --git a/frontend/src/components/ssoTab.jsx b/frontend/src/components/ssoTab.jsx index 8f39a296..5034f521 100644 --- a/frontend/src/components/ssoTab.jsx +++ b/frontend/src/components/ssoTab.jsx @@ -7,11 +7,7 @@ import { Tooltip, TextField, Grid, - Skeleton, - Dialog, - DialogTitle, - DialogContent, - Box, + Checkbox } from "@mui/material"; import { makeStyles } from "@mui/styles"; import { Link } from "react-router-dom"; @@ -30,6 +26,8 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle const classes = useStyles(); const [show2faSetup, setShow2faSetup] = React.useState(false); const [autoPrivision, setAutoProvision] = React.useState(selectedOrganization?.sso_config?.auto_provision) + const [showOpenIdCred, setShowOpenIdCred] = React.useState(false); + const [showSamlCred, setShowSamlCred] = React.useState(false); const [ssoEntrypoint, setSsoEntrypoint] = React.useState( selectedOrganization.sso_config === undefined ? "" @@ -393,7 +391,27 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle IdP URL for Shuffle OpenID: {`${globalUrl}/api/v1/login_openid`} - + +
+ + Show OpenID Credentials + + setShowOpenIdCred(e.target.checked)} + name="showOpenIdCred" + color="primary" + style={{ color: "rgba(255, 255, 255, 1)", }} + /> +
@@ -407,30 +425,29 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, }} fullWidth={true} - type="name" multiline={true} rows={2} id="outlined-with-placeholder" margin="normal" variant="outlined" placeholder="The OpenID client ID from the identity provider" - value={openidClientId} - onChange={(e) => { - setOpenidClientId(e.target.value); - }} + value={showOpenIdCred ? openidClientId : openidClientId?.length > 0 ? "•".repeat(50) : ""} + onChange={(e) => setOpenidClientId(e.target.value)} + onFocus={(e) => setShowOpenIdCred(true)} + onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { - notchedOutline: isEditOrgTab ? null : classes.notchedOutline, + notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { - color: "white", - fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", - fontWeight: 400, - fontSize: 16, - borderRadius: 4, + color: "white", + fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", + fontWeight: 400, + fontSize: 16, + borderRadius: 4, }, }} - /> + /> @@ -445,17 +462,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, }} fullWidth={true} - type="name" + type={showOpenIdCred ? "text" : "password"} multiline={true} rows={2} id="outlined-with-placeholder" margin="normal" variant="outlined" placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE" - value={openidClientSecret} + value={showOpenIdCred ? openidClientSecret : openidClientSecret?.length > 0 ? "•".repeat(50) : ""} onChange={(e) => { setOpenidClientSecret(e.target.value); - }} + }} + onFocus={(e) => setShowOpenIdCred(true)} + onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, @@ -485,17 +504,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, }} fullWidth={true} - type="name" + type={showOpenIdCred ? "text" : "password"} id="outlined-with-placeholder" margin="normal" variant="outlined" multiline={true} rows={2} placeholder="The OpenID authorization URL (usually ends with /authorize)" - value={openidAuthorization} + value={showOpenIdCred ? openidAuthorization : openidAuthorization?.length > 0 ? "•".repeat(50) : ""} onChange={(e) => { setOpenidAuthorization(e.target.value) - }} + }} + onFocus={(e) => setShowOpenIdCred(true)} + onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, @@ -523,17 +544,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, }} fullWidth={true} - type="name" + type={showOpenIdCred ? "text" : "password"} id="outlined-with-placeholder" margin="normal" variant="outlined" multiline={true} rows={2} placeholder="The OpenID token URL (usually ends with /token)" - value={openidToken} + value={showOpenIdCred ? openidToken : openidToken?.length > 0 ? "•".repeat(50) : ""} onChange={(e) => { setOpenidToken(e.target.value) - }} + }} + onFocus={(e) => setShowOpenIdCred(true)} + onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, @@ -557,7 +580,27 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle SAML SSO (v1.1) IdP URL for Shuffle SAML/SSO: {`${globalUrl}/api/v1/login_sso`} - + +
+ + Show SAML Credentials + + setShowSamlCred(e.target.checked)} + name="showSamlCred" + color="primary" + style={{ color: "rgba(255, 255, 255, 1)", }} + /> +
@@ -571,17 +614,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, }} fullWidth={true} - type="name" + type={showSamlCred ? "text" : "password"} multiline={true} rows={2} id="outlined-with-placeholder" margin="normal" variant="outlined" placeholder="The entrypoint URL from your provider" - value={ssoEntrypoint} + value={showSamlCred ? ssoEntrypoint : ssoEntrypoint?.length > 0 ? "•".repeat(50) : ""} onChange={(e) => { setSsoEntrypoint(e.target.value); - }} + }} + onFocus={(e) => setShowSamlCred(true)} + onBlur={(e) => setShowSamlCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, @@ -609,14 +654,16 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, }} fullWidth={true} - type="name" + type={showSamlCred ? "text" : "password"} id="outlined-with-placeholder" margin="normal" variant="outlined" multiline={true} rows={2} placeholder="The X509 certificate to use" - value={ssoCertificate} + value={showSamlCred ? ssoCertificate : ssoCertificate?.length > 0 ? "•".repeat(50) : ""} + onFocus={(e) => setShowSamlCred(true)} + onBlur={(e) => setShowSamlCred(false)} onChange={(e) => { setSsoCertificate(e.target.value); }} @@ -646,4 +693,4 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle ) } -export default SSOTab \ No newline at end of file +export default SSOTab diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index ede54153..9bcc6966 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -5,8 +5,8 @@ import { useNavigate, Link, useParams } from "react-router-dom"; import { isMobile } from "react-device-detect"; import { toast } from 'react-toastify'; import { useInterval } from "react-powerhooks"; - - +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; +import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import { ConnectingAirportsOutlined, ConstructionOutlined, @@ -81,21 +81,6 @@ const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important" }, - marketplaceButton: { - width: "100%", - justifyContent: "flex-start", - padding: "12px", - marginBottom: "12px", - backgroundColor: "#1A1A1A", - border: "1px solid #494949", - borderRadius: "8px", - color: "white", - opacity: 0.7, - '&:hover': { - opacity: 1, - cursor: "not-allowed", - }, - }, marketplaceIcon: { width: 28, height: 28, @@ -174,6 +159,14 @@ const FreePlanCard = ({ classes }) => { const MarketplaceCard = ({ classes }) => { const marketplaceOptions = [ + { + name: "Open Source Install", + logo: "https://static.cdnlogo.com/logos/g/69/github-icon.svg", + tooltipText: "Click to install!", + valid: true, + + link: "https://github.com/shuffle/shuffle/blob/main/.github/install-guide.md" + }, { name: "Amazon Web Services", logo: "https://cdn.cdnlogo.com/logos/a/19/aws.svg", @@ -213,31 +206,50 @@ const MarketplaceCard = ({ classes }) => { Self Host - {marketplaceOptions.map((option, index) => ( - - - - ))} + style={{ + width: "100%", + justifyContent: "flex-start", + padding: "12px", + marginBottom: "12px", + backgroundColor: "#1A1A1A", + border: "1px solid #494949", + borderRadius: "8px", + color: "white", + opacity: 0.7, + cursor: option.valid === true ? "pointer" : "not-allowed", + }} + disabled={option.valid === false} + > + {option.name} + + {option.name} + + + + ) + })}
{ */} @@ -282,7 +294,7 @@ const LoginPage = props => { const [register, setRegister] = useState(inregister); const [checkboxClicked, setCheckboxClicked] = useState(false); const [loginWithSSO, setLoginWithSSO] = useState(false) - + const [showPassword, setShowPassword] = useState(false) const [ssoUrl, setSSOUrl] = useState(""); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io"; @@ -315,7 +327,15 @@ const LoginPage = props => { } else { document.title = "Register to Shuffle SaaS" } - } + } + + useEffect(() => { + + if (loginWithSSO && window?.location?.pathname === "/register") { + setLoginWithSSO(false) + } + + },[window?.location?.pathname]) // Just a way to force location loading properly // Register & login should be split :3 @@ -354,7 +374,8 @@ const LoginPage = props => { color: "white", padding: "40px", flex: 1, - maxWidth: isMobile ? "100%" : "550px", + maxWidth: isMobile ? "100%" : 410, + minWidth: 410, background: "#212121", borderRadius: "12px", display: "flex", @@ -649,26 +670,16 @@ const LoginPage = props => { setLoginWithSSO(true) } - //const onClickRegister = () => { - // if (props.location.pathname === "/login") { - // window.location.pathname = "/register" - // } else { - // window.location.pathname = "/login" - // } - - // setLoginCheck(!register) - //} - - //var loginChange = register ? (

Want to register? Click here.

) : (

Go back to login? Click here.

); var formtitle = register ?
Welcome Back!
:
Create your account
var formButton = !isCloud ? "" : register ?
Don’t have an account yet?
Register here
: <> +
Already have an account?
Login here
//
Click here to Login
// {formtitle} - const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)" + const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)" const buttonStyle = { borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(username, password) || loginLoading || (checkboxClicked && register) ? buttonBackground : "grey", color: "white" } //
- {ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? ( + {( + ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length) > 0 + //|| (isCloud && !loginWithSSO && window?.location?.pathname !== "/register") + ? (
Or
@@ -916,11 +944,17 @@ const LoginPage = props => { color="secondary" variant="outlined" type="button" - style={{ flex: "1", marginTop: 5 }} - onClick={() => { + style={{ flex: "1", marginTop: 5, textTransform: 'none', fontSize: 16 }} + onClick={(e) => { //console.log("CLICK SSO"); - window.location.href = ssoUrl + e.preventDefault(); //navigate(ssoUrl) + if (isCloud) { + setLoginWithSSO(true) + setPassword("") + }else { + window.location.href = ssoUrl + } }} > Use SSO @@ -928,6 +962,19 @@ const LoginPage = props => {
) : null} + {isCloud && loginWithSSO && ( + + )} @@ -951,7 +998,7 @@ const LoginPage = props => {
return ( -
+
{loadedCheck}
) From 154061c2ed6ab99f8097e5d17ef3c75601c936b7 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 18 Mar 2025 19:46:50 +0100 Subject: [PATCH 079/200] Create dependabot.yml --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5990d9c6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From 6ffbd2598656f7a455a02100458b8943d2add848 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 18:47:57 +0000 Subject: [PATCH 080/200] build(deps): bump the go_modules group across 3 directories with 2 updates Bumps the go_modules group with 2 updates in the /backend/go-app directory: [github.com/containerd/containerd](https://github.com/containerd/containerd) and [golang.org/x/net](https://github.com/golang/net). Bumps the go_modules group with 1 update in the /functions/onprem/orborus directory: [golang.org/x/net](https://github.com/golang/net). Bumps the go_modules group with 1 update in the /functions/onprem/worker directory: [golang.org/x/net](https://github.com/golang/net). Updates `github.com/containerd/containerd` from 1.6.26 to 1.6.38 - [Release notes](https://github.com/containerd/containerd/releases) - [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md) - [Commits](https://github.com/containerd/containerd/compare/v1.6.26...v1.6.38) Updates `golang.org/x/net` from 0.34.0 to 0.36.0 - [Commits](https://github.com/golang/net/compare/v0.34.0...v0.36.0) Updates `golang.org/x/net` from 0.34.0 to 0.36.0 - [Commits](https://github.com/golang/net/compare/v0.34.0...v0.36.0) Updates `golang.org/x/net` from 0.25.0 to 0.36.0 - [Commits](https://github.com/golang/net/compare/v0.34.0...v0.36.0) --- updated-dependencies: - dependency-name: github.com/containerd/containerd dependency-type: indirect dependency-group: go_modules - dependency-name: golang.org/x/net dependency-type: indirect dependency-group: go_modules - dependency-name: golang.org/x/net dependency-type: indirect dependency-group: go_modules - dependency-name: golang.org/x/net dependency-type: indirect dependency-group: go_modules ... Signed-off-by: dependabot[bot] --- backend/go-app/go.mod | 17 +++-- backend/go-app/go.sum | 126 +++++++++++++------------------- functions/onprem/orborus/go.mod | 14 ++-- functions/onprem/orborus/go.sum | 36 ++++----- functions/onprem/worker/go.mod | 32 ++++---- functions/onprem/worker/go.sum | 119 +++++++++--------------------- 6 files changed, 129 insertions(+), 215 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index cf2eb962..261a71b4 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,7 @@ module shuffle go 1.22.2 +toolchain go1.24.1 //replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared @@ -19,7 +20,7 @@ require ( github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 github.com/shuffle/shuffle-shared v0.8.12 - golang.org/x/crypto v0.32.0 + golang.org/x/crypto v0.35.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.68.1 gopkg.in/src-d/go-git.v4 v4.13.1 @@ -37,6 +38,7 @@ require ( cloud.google.com/go/iam v1.1.7 // indirect cloud.google.com/go/scheduler v1.10.6 // indirect dario.cat/mergo v1.0.0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -47,7 +49,6 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/containerd/containerd v1.6.26 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -114,16 +115,18 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v0.11.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.34.0 // indirect + golang.org/x/net v0.36.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 99179e7f..331a76de 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -15,8 +15,6 @@ cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKF cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -37,8 +35,6 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 h1:V8krnnfGj4pV65YLUm3C0/8bl7V5Nry2Pwvy3ru/wLc= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -48,8 +44,6 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.9.10 h1:TxXGNmcbQxBKVWvjvTocNb6jrPyeHlk5EiDhhgHgggs= -github.com/Microsoft/hcsshim v0.9.10/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= @@ -61,6 +55,7 @@ github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= @@ -90,6 +85,7 @@ github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -97,14 +93,17 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/containerd v1.6.26 h1:VVfrE6ZpyisvB1fzoY8Vkiq4sy+i5oF4uk7zu03RaHs= -github.com/containerd/containerd v1.6.26/go.mod h1:I4TRdsdoo5MlKob5khDJS2EPT1l1oMNaE2MBm6FrwxM= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -112,12 +111,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g= -github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -132,6 +127,8 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -157,11 +154,12 @@ github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -196,6 +194,7 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -212,6 +211,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -297,8 +297,6 @@ github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -316,8 +314,6 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= @@ -335,9 +331,10 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6V7LB6EL8= github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= @@ -350,12 +347,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdR github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.4 h1:R/A62IzHJXnhVNG1PqFXN1YtRgdiiSIL5q4YJEef5P8= -github.com/shuffle/shuffle-shared v0.8.4/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.8.5 h1:c98AMOzIrDXmgQrKyf1HJ9wZwAwB02LPRvf9+yy05OQ= -github.com/shuffle/shuffle-shared v0.8.5/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.8.6 h1:chnGJRKsO1gR5Vt/8hp5SQg1yP8/DmiK8sRhWVaTgEg= -github.com/shuffle/shuffle-shared v0.8.6/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/shuffle/shuffle-shared v0.8.12 h1:fg6jGAuevOGLgR7kpoZDJh//tzFjrEqj/bpxRFzxUTw= github.com/shuffle/shuffle-shared v0.8.12/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -365,7 +356,6 @@ github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0 github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= @@ -401,30 +391,25 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0 h1:Ydage/P0fRrSPpZeCVxzjqGcI6iVmG2xb43+IR8cjqM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= @@ -438,10 +423,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -488,6 +471,7 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -496,17 +480,13 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= +golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -517,8 +497,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -534,10 +514,12 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -545,18 +527,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -567,8 +545,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -584,7 +562,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -640,15 +617,12 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c h1:kaI7oewGK5YnVwj+Y+EJBO/YN1ht8iTL9XkFHtVZLsc= -google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -659,9 +633,10 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -675,8 +650,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -696,6 +670,7 @@ gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQb gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -703,9 +678,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= -gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 7909c2ee..20c60f10 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -1,7 +1,7 @@ module orborus go 1.22.0 -toolchain go1.23.6 +toolchain go1.24.1 //replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared @@ -96,14 +96,14 @@ require ( go.opentelemetry.io/otel/trace v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/crypto v0.32.0 // indirect + golang.org/x/crypto v0.35.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.34.0 // indirect + golang.org/x/net v0.36.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 2bb0c986..169e266a 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -123,12 +123,10 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= - github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= - github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -303,12 +301,11 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.6.99 h1:sPGmZo+8JMgUH9Q2O659za2w4sF/NXiWFCrSPm1nTAU= -github.com/shuffle/shuffle-shared v0.6.99/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= +github.com/shuffle/shuffle-shared v0.8.7 h1:+UdRx7b/KUy/E92ODNr87UvCI2Dxfnv0samQWuggJu4= +github.com/shuffle/shuffle-shared v0.8.7/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= - github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -369,9 +366,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= - -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -427,9 +423,8 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= - -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= +golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -437,7 +432,6 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= - golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -446,8 +440,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -474,16 +468,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= - -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= - -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -494,8 +486,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 5b8cbd22..15dd0e31 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -1,8 +1,7 @@ module worker go 1.22.7 - -toolchain go1.22.11 +toolchain go1.24.1 require ( github.com/docker/docker v27.5.0+incompatible @@ -16,13 +15,13 @@ require ( require ( cloud.google.com/go v0.112.0 // indirect - cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/datastore v1.15.0 // indirect cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/scheduler v1.10.6 // indirect cloud.google.com/go/storage v1.36.0 // indirect dario.cat/mergo v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v1.1.3 // indirect @@ -30,8 +29,8 @@ require ( github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect @@ -46,8 +45,7 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.0 // indirect github.com/go-git/go-git/v5 v5.13.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect - + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -70,10 +68,8 @@ require ( github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect @@ -86,6 +82,7 @@ require ( github.com/sendgrid/rest v2.6.9+incompatible // indirect github.com/sendgrid/sendgrid-go v3.14.0+incompatible // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -95,19 +92,19 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.33.0 // indirect go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.35.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/net v0.36.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/api v0.162.0 // indirect @@ -121,7 +118,6 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.5.1 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum index bb907d1c..a20221a8 100644 --- a/functions/onprem/worker/go.sum +++ b/functions/onprem/worker/go.sum @@ -11,10 +11,6 @@ cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -33,8 +29,8 @@ cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYE dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -42,7 +38,6 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= - github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= @@ -81,12 +76,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= - github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -94,8 +88,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g= -github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -112,8 +104,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s= @@ -126,19 +118,15 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= - github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= - github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -225,8 +213,8 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56 github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -260,8 +248,7 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -294,8 +281,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6V7LB6EL8= github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= @@ -305,20 +292,10 @@ github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekuei github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA= github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shuffle/shuffle-shared v0.6.90 h1:FzIYtEt44eWgEsW/9tj2ki7qq8FEm/HWXUok+THp72M= -github.com/shuffle/shuffle-shared v0.6.90/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= -github.com/shuffle/shuffle-shared v0.7.0 h1:T7nbC5An/K9ePx1pwvYqSnnNZuafwK3GVy6wPDuog18= -github.com/shuffle/shuffle-shared v0.7.0/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.7.82 h1:La11F5jp9bNtM3VuR9PawyWo90/vZ+1Txo4NIc4YdPg= -github.com/shuffle/shuffle-shared v0.7.82/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.7.83 h1:OyyDo0ii8rOYHN5wGbcM94JuDKLmbZ9jhMQ0+/KMb0A= -github.com/shuffle/shuffle-shared v0.7.83/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.8.0 h1:8ABtFz5UYOXN6mnnu/LrOwz8TxASnu1OBbj+gfqmtYk= -github.com/shuffle/shuffle-shared v0.8.0/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= -github.com/shuffle/shuffle-shared v0.8.3 h1:15GVPTpQnA6j2AkwpjkQK8iVxMoEPW336uMIVvAWgBQ= -github.com/shuffle/shuffle-shared v0.8.3/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shuffle/shuffle-shared v0.8.7 h1:+UdRx7b/KUy/E92ODNr87UvCI2Dxfnv0samQWuggJu4= +github.com/shuffle/shuffle-shared v0.8.7/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -357,32 +334,22 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= -go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= -go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -392,10 +359,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -425,7 +390,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -451,16 +415,13 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= +golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -471,9 +432,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -493,21 +453,20 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -518,9 +477,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -552,7 +510,6 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -594,12 +551,8 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -611,8 +564,6 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= -google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -626,8 +577,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 95f6e3c698a378a854797b4c40b33e72ce6dc745 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 20 Mar 2025 17:22:54 +0100 Subject: [PATCH 081/200] Remapped back to using SHUFFLE_BASE_IMAGE_NAME with default frikky/shuffle: https://github.com/Shuffle/Shuffle/issues/1660 --- functions/onprem/worker/worker.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 943dcd2f..16e2b76e 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -57,11 +57,10 @@ var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME") var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION")) -var baseimagename = "frikky/shuffle" var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") var executionCount int64 -// var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") +var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") // var baseimagename = "registry.hub.docker.com/frikky/shuffle" var registryName = "registry.hub.docker.com" @@ -3724,7 +3723,7 @@ func main() { } if baseimagename == "" { - log.Printf("[DEBUG] Setting baseimagename") + log.Printf("[DEBUG] Setting baseimagename to frikky/shuffle") baseimagename = "frikky/shuffle" // Dockerhub //baseimagename = "shuffle" // Github (ghcr.io) } From 373e5bc9092161acf1330cdf3d89dd9c841d42a8 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 21 Mar 2025 02:03:19 +0100 Subject: [PATCH 082/200] Multiple minor fixes for 2.0.1 release to improve general usability --- frontend/src/components/LeftSideBar.jsx | 10 +- frontend/src/components/LicencePopup.jsx | 11 +- frontend/src/components/Navbar.jsx | 2 +- .../src/components/OrgHeaderexpandedNew.jsx | 48 +++++--- frontend/src/components/ParsedAction.jsx | 2 +- .../src/components/ShuffleCodeEditor1.jsx | 3 +- .../components/WorkflowValidationTimeline.jsx | 10 +- frontend/src/theme.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 107 ++++++++++++------ frontend/src/views/Docs.jsx | 7 +- frontend/src/views/RunWorkflow.jsx | 2 + frontend/src/views/Workflows2.jsx | 17 ++- 12 files changed, 145 insertions(+), 76 deletions(-) diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 6ef36775..baf60f06 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -526,7 +526,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { - Version: 2.0.0 + Version: 2.0.1 @@ -773,6 +773,9 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }, [window?.location?.pathname]); + const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + + return (
{ transition: "width 0.3s ease", boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.2)" , resize: 'both', - - zoom: 0.8, + zoom: isSafari ? undefined : 0.8, + transform: isSafari ? "scale(0.8)" : undefined, + transformOrigin: isSafari ? "top left" : undefined, height: "calc((100vh - 32px)*1.2)", }} > diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index 9492ae99..67fb07b4 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -909,21 +909,23 @@ const LicencePopup = (props) => { const priceItem = window.location.origin === "https://shuffler.io/" || "https://sandbox.shuffler.io/" ? shuffleVariant === 0 - ? "app_executions" - : "cores" + ? "price_1PWI3uDzMUgUjxHSffUBwWCy" + : "price_1PWI8EDzMUgUjxHSfEhUB7oL" + : shuffleVariant === 0 ? "price_1PZPSSEJjT17t98NLJoTMYja" : "price_1PZPQuEJjT17t98N3yORUtd9"; const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success` const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure` + const quantity = shuffleVariant === 0 ? selectedValue / 100 : selectedValue - console.log("Priceitem: ", priceItem, shuffleVariant) + console.log("Priceitem: ", priceItem, quantity, shuffleVariant) var checkoutObject = { lineItems: [ { price: priceItem, - quantity: shuffleVariant === 0 ? selectedValue / 100 : selectedValue, + quantity: quantity, }, ], mode: "subscription", @@ -1141,6 +1143,7 @@ const LicencePopup = (props) => { > View all plans +
diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index e97e6ac2..1dad55c4 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1,7 +1,7 @@ -import React, { useState, useEffect, useLayoutEffect, useMemo } from "react"; +import React, { useState, useEffect, useContext, useMemo } from "react"; import { toast } from 'react-toastify'; import { makeStyles, createStyles } from "@mui/styles"; -import theme from '../theme.jsx'; +import {getTheme} from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; @@ -11,7 +11,7 @@ import { NestedMenuItem } from "mui-nested-menu"; import { parsedDatatypeImages } from "../components/AppFramework.jsx"; import { green, yellow, red } from "../views/AngularWorkflow.jsx" //import { useAlert - +import { Context } from "../context/ContextApi.jsx"; import { Chip, ButtonGroup, @@ -215,6 +215,9 @@ const ParsedAction = (props) => { } }, [expansionModalOpen]) + const {themeMode, supportEmail} = useContext(Context) + const theme = getTheme(themeMode) + /* useEffect(() => { // This will have the OLD selectedAction, not the new one huh? @@ -1626,7 +1629,7 @@ const ParsedAction = (props) => { padding: 8, paddingLeft: 14, paddingBottom: 4, - backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, + backgroundColor: hover ? theme.palette.hoverColor : theme.palette.textFieldStyle.backgroundColor, }} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} onClick={(event) => { // event.preventDefault() @@ -1664,7 +1667,7 @@ const ParsedAction = (props) => { {newActionname}
{extraDescription.length > 0 ? - + {extraDescription} : null} @@ -1738,14 +1741,14 @@ const ParsedAction = (props) => { }} > - + @@ -1809,7 +1812,7 @@ const ParsedAction = (props) => { title="See previous results for this action" placement="top" > - + { title="Find app documentation" placement="top" > - + @@ -1863,7 +1866,7 @@ const ParsedAction = (props) => { {autoCompleting ? : - + } @@ -1883,7 +1886,7 @@ const ParsedAction = (props) => { marginTop: "auto", marginBottom: "auto", height: 30, - marginLeft: 115, + marginLeft: 98, textTransform: "none", }} disabled={autoCompleting} @@ -1891,7 +1894,7 @@ const ParsedAction = (props) => { if (runFromHere !== undefined) { runFromHere(selectedAction) } else { - toast.error("Function not available. Please contact support@shuffler.io") + toast.error(`Function not available. Please contact ${supportEmail}`) } }} > @@ -1939,6 +1942,12 @@ const ParsedAction = (props) => { { } }} style={{ - backgroundColor: theme.palette.inputColor, - color: "white", + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, height: 35, maxWidth: rightsidebarStyle.maxWidth - 80, borderRadius: theme.palette?.borderRadius, }} > @@ -2460,11 +2486,14 @@ const ParsedAction = (props) => { return ( @@ -2472,7 +2501,7 @@ const ParsedAction = (props) => { {data?.validation?.valid === true ? { : null} {data?.last_modified === true ? { { {workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? (
- Runtime variable (optional) + Runtime variable (optional) { return ( @@ -4690,7 +4747,7 @@ const ParsedAction = (props) => { width: 24, height: 24, marginRight: 10, - color: "rgba(255,255,255,0.6)", + color: theme.palette.textPrimary, }} onClick={() => { setAuthenticationModalOpen(true); @@ -4708,7 +4765,7 @@ const ParsedAction = (props) => { > @@ -4720,7 +4777,7 @@ const ParsedAction = (props) => { placement="top" > @@ -4747,7 +4804,7 @@ const ParsedAction = (props) => { flex: "10", marginTop: "auto", marginBottom: "auto", - color: "#C5C5C5", + color: theme.palette.textPrimary, }} > {tmpitem} {selectedActionParameters[count].required || selectedActionParameters[count].configuration ? "*" : ""} @@ -4776,7 +4833,7 @@ const ParsedAction = (props) => { {((data.options !== undefined && data.options !== null && data.options.length > 0) || (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0)) ? null : { const clickedField = document.getElementById(clickedFieldId) if (clickedField !== null) { diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 0362a59f..e8d83016 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -1,7 +1,6 @@ import React, { useState, useEffect, useContext, memo } from "react"; - import { toast } from "react-toastify"; -import theme from "../theme.jsx"; +import { getTheme } from "../theme.jsx"; import { v4 as uuidv4, v5 as uuidv5, validate as isUUID, } from "uuid"; import { Paper, @@ -18,6 +17,7 @@ import { TextField, MenuItem, IconButton, + Box, } from "@mui/material"; import { @@ -41,7 +41,8 @@ const useStyles = makeStyles({ const Priorities = memo((props) => { const { globalUrl, userdata,clickedFromOrgTab,selectedOrganization, handleEditOrg, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, notifications, setNotifications, } = props; - + const { themeMode, brandColor } = useContext(Context); + const theme = getTheme(themeMode, brandColor); const [showDismissed, setShowDismissed] = React.useState(false); const [showRead, setShowRead] = React.useState(false); const [appFramework, setAppFramework] = React.useState({}); @@ -62,7 +63,7 @@ const Priorities = memo((props) => { ); let navigate = useNavigate(); - const classes = useStyles(); + const classes = useStyles(); useEffect(() => { getFramework() @@ -353,13 +354,13 @@ const Priorities = memo((props) => { } return ( -
-
+
+
- + Notification Workflow - + The notification workflow triggers when an error occurs in one of your workflows. Each individual one will only start a workflow once every 2 minutes. You can point child org notifications into the parent org notification by choosing it in the list. @@ -382,8 +383,9 @@ const Priorities = memo((props) => { classes={{ inputRoot: classes.inputRoot }} ListboxProps={{ style: { - backgroundColor: "#212121", - color: "white", + backgroundColor: theme.palette.surfaceColor, + color: theme.palette.text.primary, + borderRadius: theme.palette.borderRadius, }, }} getOptionLabel={(option) => { @@ -404,8 +406,9 @@ const Priorities = memo((props) => { options={workflows} fullWidth style={{ - backgroundColor: "#212121", - borderRadius: theme.palette?.borderRadius, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + borderRadius: theme.palette.textFieldStyle.borderRadius, + color: theme.palette.textFieldStyle.color, height: 35, marginBottom: 40, }} @@ -449,8 +452,8 @@ const Priorities = memo((props) => { { { } }} style={{ - backgroundColor: "rgba(33, 33, 33, 1)", + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, borderRadius: 4, height: 35, fontSize: 16, @@ -548,7 +553,7 @@ const Priorities = memo((props) => { {notificationWorkflow === undefined || notificationWorkflow === null || notificationWorkflow.length === 0 ? null :
-
} - Notifications ({ + Notifications ({ notifications?.filter((notification) => showRead === true || notification.read === false).length }) - + Notifications help you find potential problems with your workflows and apps.  Learn more - +
{ onChange={() => { setShowRead(!showRead); }} - />  Show read + />  Show read {notifications !== undefined && notifications !== null && notifications.length > 1 ? ( + {data.read === false ? (
- + ); }) diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 4bd3a6f2..7cc0f3da 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -1,10 +1,10 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { toast } from 'react-toastify'; - +import { getTheme } from "../theme.jsx"; import ReactGA from 'react-ga4'; -import theme from "../theme.jsx"; import { useNavigate, Link } from "react-router-dom"; import { findSpecificApp } from "../components/AppFramework.jsx" +import { Context } from "../context/ContextApi.jsx"; import { Paper, Typography, @@ -23,7 +23,8 @@ import { const Priority = (props) => { const { globalUrl, clickedFromOrgTab,userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props; - + const { themeMode, supportEmail } = useContext(Context); + const theme = getTheme(themeMode); const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); let navigate = useNavigate(); @@ -113,7 +114,7 @@ const Priority = (props) => { } }) .catch((error) => { - toast("Failed dismissing alert. Please contact support@shuffler.io if this persists."); + toast(`Failed dismissing alert. Please contact ${supportEmail} if this persists.`); }); } @@ -121,10 +122,10 @@ const Priority = (props) => { const srcSize = realignedSrc ? 35 : 30 const dstSize = realignedDst ? 35 : 30 return ( -
+
- {priority.type === "usecase" || priority.type == "apps" ? : null} + {priority.type === "usecase" || priority.type == "apps" ? : null} {priority.name} @@ -138,7 +139,7 @@ const Priority = (props) => { {newdescription.split("&").length > 3 ? - + {priority.name+"2"} {newdescription.split("&")[2]} @@ -154,7 +155,7 @@ const Priority = (props) => { }
- {priority.active === true ? -
@@ -859,7 +865,7 @@ const TenantsTab = memo((props) => { /> */} -
+
{ display: "table-cell", padding: "0px 8px 8px 8px", textAlign: "center", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -903,7 +909,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", textAlign: "center", }} @@ -915,7 +921,7 @@ const TenantsTab = memo((props) => { minWidth: 100, maxWidth: 100, display: "table-cell", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, padding: "0px 8px 8px 8px", }} /> @@ -929,7 +935,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -942,7 +948,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -954,7 +960,7 @@ const TenantsTab = memo((props) => { key={rowIndex} style={{ display: "flex", - backgroundColor: "#212121", + backgroundColor: theme.palette.platformColor, height: 30, }} > @@ -976,7 +982,7 @@ const TenantsTab = memo((props) => { variant="text" animation="wave" sx={{ - backgroundColor: "#1a1a1a", + backgroundColor: theme.palette.loaderColor, borderRadius: "4px", }} /> @@ -987,7 +993,7 @@ const TenantsTab = memo((props) => { ) : parentOrg?.id?.length > 0 ? ( { padding: "10px", whiteSpace: "nowrap", }} - primary={index === 1 ? "Parent Organization not found or May be you are not part of parent org. Please contact support@shuffler.io." : null} + primary={index === 1 ? `Parent Organization not found or May be you are not part of parent org. Please contact ${supportEmail}` : null} colSpan={index === 0 ? 5 : undefined} /> ))} @@ -1123,17 +1129,15 @@ const TenantsTab = memo((props) => { marginTop: 20, }} > -

Sub Organizations of the Current Organization ({subOrgs.length}) -

+
{/* { }} /> */} -
+
{ display: "table-cell", padding: "0px 8px 8px 8px", textAlign: "center", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -1214,7 +1218,7 @@ const TenantsTab = memo((props) => { display: "table-cell", padding: "0px 8px 8px 8px", textAlign: "center", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", textAlign: "center", }} /> @@ -1237,7 +1241,7 @@ const TenantsTab = memo((props) => { minWidth: 100, maxWidth: 100, display: "table-cell", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, padding: "0px 8px 8px 8px", }} /> @@ -1251,7 +1255,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -1264,7 +1268,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -1290,9 +1294,13 @@ const TenantsTab = memo((props) => { } } } + var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF"; + if (index % 2 === 0) { + bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA"; + } return ( - + } style={{ width: 100, minWidth: 100, maxWidth: 100, @@ -1374,17 +1382,15 @@ const TenantsTab = memo((props) => { />
-

All Tenants -

+
{/* { style={{ borderRadius: 4, marginTop: 24, - border: "1px solid #494949", + border: theme.palette.defaultBorder, width: "100%", overflowX: "auto", paddingBottom: 0, @@ -1448,7 +1454,7 @@ const TenantsTab = memo((props) => { display: "table-cell", padding: "0px 8px 8px 8px", textAlign: "center", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -1475,7 +1481,7 @@ const TenantsTab = memo((props) => { display: "table-cell", padding: "0px 8px 8px 8px", textAlign: "center", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -1488,7 +1494,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", textAlign: "center", }} @@ -1500,7 +1506,7 @@ const TenantsTab = memo((props) => { minWidth: 100, maxWidth: 100, display: "table-cell", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, padding: "0px 8px 8px 8px", }} /> @@ -1514,7 +1520,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -1527,7 +1533,7 @@ const TenantsTab = memo((props) => { padding: "0px 8px 8px 8px", whiteSpace: "nowrap", textOverflow: "ellipsis", - borderBottom: "1px solid #494949", + borderBottom: theme.palette.defaultBorder, verticalAlign: "middle", }} /> @@ -1590,6 +1596,11 @@ const TenantsTab = memo((props) => { } } + var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF"; + if (index % 2 === 0) { + bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA"; + } + return ( { display: "table-row", verticalAlign: "middle", padding: 8, - backgroundColor: index % 2 === 0 ? "#1A1A1A" : "#212121", + backgroundColor: bgColor, borderBottomLeftRadius: userdata?.orgs?.length - 1 === index ? 8 : 0, borderBottomRightRadius: diff --git a/frontend/src/components/UserManagmentTab.jsx b/frontend/src/components/UserManagmentTab.jsx index bb6870ff..ec7036cc 100644 --- a/frontend/src/components/UserManagmentTab.jsx +++ b/frontend/src/components/UserManagmentTab.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useContext, memo } from "react"; import { toast } from 'react-toastify'; - +import { Context } from "../context/ContextApi.jsx"; import { FormControl, InputLabel, @@ -35,7 +35,7 @@ import { import ModeEditOutlineOutlinedIcon from '@mui/icons-material/ModeEditOutlineOutlined'; import ContentCopyOutlinedIcon from '@mui/icons-material/ContentCopyOutlined'; -import theme from "../theme.jsx"; +import {getTheme} from "../theme.jsx"; const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { @@ -75,12 +75,17 @@ const UserManagmentTab = memo((props) => { const [logsViewModal, setLogsViewModal] = React.useState(false); const [ipSelected, setIpSelected] = React.useState(""); const [userLogViewing, setUserLogViewing] = React.useState({}); + const { themeMode, supportEmail, brandColor } = useContext(Context); + + const theme = getTheme(themeMode, brandColor); + useEffect(() => { if (selectedOrganization?.mfa_required !== MFARequired) { setMFARequired(selectedOrganization?.mfa_required); } }, [selectedOrganization]); + useEffect(() => { if(users?.length === 0){ getUsers(); } }, []); @@ -363,7 +368,7 @@ const UserManagmentTab = memo((props) => { toast("Failed to deactivate user: " + responseJson.reason); } else if (responseJson.success === false) { toast( - "Failed to deactivate user. Please contact support@shuffler.io if this persists.", + `Failed to deactivate user. Please contact ${supportEmail} if this persists.`, ); } else { toast("Changed activation for user " + data.id); @@ -602,7 +607,7 @@ const UserManagmentTab = memo((props) => { }} > - + Add user @@ -619,7 +624,7 @@ const UserManagmentTab = memo((props) => { InputProps={{ style: { height: "50px", - color: "white", + color: theme.palette.textFieldStyle.color, fontSize: "1em", }, }} @@ -682,17 +687,17 @@ const UserManagmentTab = memo((props) => {
{loginInfo} - + @@ -751,7 +755,7 @@ const UserManagmentTab = memo((props) => { }} > - + Editing {selectedUser.username} @@ -845,9 +849,10 @@ const UserManagmentTab = memo((props) => { backgroundColor: theme.palette.inputColor, }} /> -
- +
{isCloud && userdata.support && selectedUser.id !== userdata.id ? (
- + {parsedTitle}
diff --git a/frontend/src/components/ssoTab.jsx b/frontend/src/components/ssoTab.jsx index 5034f521..770304a2 100644 --- a/frontend/src/components/ssoTab.jsx +++ b/frontend/src/components/ssoTab.jsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useContext } from "react"; import React from "react"; import { Typography, @@ -13,6 +13,8 @@ import { makeStyles } from "@mui/styles"; import { Link } from "react-router-dom"; import theme from "../theme.jsx"; import { toast } from "react-toastify"; +import { Context } from "../context/ContextApi.jsx"; +import { getTheme } from "../theme.jsx"; const useStyles = makeStyles({ notchedOutline: { @@ -26,6 +28,7 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle const classes = useStyles(); const [show2faSetup, setShow2faSetup] = React.useState(false); const [autoPrivision, setAutoProvision] = React.useState(selectedOrganization?.sso_config?.auto_provision) + const [roleRequired, setRoleRequired] = React.useState(selectedOrganization?.sso_config?.role_required || false); const [showOpenIdCred, setShowOpenIdCred] = React.useState(false); const [showSamlCred, setShowSamlCred] = React.useState(false); const [ssoEntrypoint, setSsoEntrypoint] = React.useState( @@ -84,6 +87,9 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle : selectedOrganization.sso_config.openid_token ) + const { themeMode, supportEmail, brandColor } = useContext(Context); + const theme = getTheme(themeMode, brandColor); + useEffect(()=>{ if (openidClientSecret !== selectedOrganization?.sso_config?.client_secret) { @@ -116,12 +122,17 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle if (autoPrivision !== selectedOrganization?.sso_config?.auto_provision) { setAutoProvision(selectedOrganization?.sso_config?.auto_provision) } + + if (roleRequired !== selectedOrganization?.sso_config?.role_required) { + setRoleRequired(selectedOrganization?.sso_config?.role_required) + } + },[selectedOrganization]) const orgSaveButton = (
- OpenID connect - + OpenID connect + Configure and Authorize SAML / SSO or OpenID connect. {" "} Learn more - - IdP URL for Shuffle OpenID: {`${globalUrl}/api/v1/login_openid`} + + IdP URL for Shuffle OpenID: {`${globalUrl}/api/v1/login_openid`}
- @@ -409,20 +466,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle onChange={(e) => setShowOpenIdCred(e.target.checked)} name="showOpenIdCred" color="primary" - style={{ color: "rgba(255, 255, 255, 1)", }} />
- Client ID + Client ID @@ -452,14 +508,14 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle - Client Secret + Client Secret @@ -494,14 +550,14 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle - Authorization URL + Authorization URL - Token URL + Token URL - SAML SSO (v1.1) - - IdP URL for Shuffle SAML/SSO: {`${globalUrl}/api/v1/login_sso`} + SAML SSO (v1.1) + + IdP URL for Shuffle SAML/SSO: {`${globalUrl}/api/v1/login_sso`}
@@ -598,20 +654,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle onChange={(e) => setShowSamlCred(e.target.checked)} name="showSamlCred" color="primary" - style={{ color: "rgba(255, 255, 255, 1)", }} />
- SSO Entrypoint (IdP) + SSO Entrypoint (IdP) - SSO Certificate (X509) + SSO Certificate (X509) { @@ -11,6 +13,14 @@ export const AppContext = (props) => { const [isDocSearchModalOpen, setIsDocSearchModalOpen] = useState(false); const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true) const [windowWidth, setWindowWidth] = useState(serverside === true ? 100 : window.innerWidth); + const [brandColor, setBrandColor] = useState(() => localStorage.getItem("brandColor") || "#ff8544"); + const [brandName, setBrandName] = useState(()=> localStorage.getItem("brandName") || "Shuffle"); + + const [themeMode, setThemeMode] = useState( + () => localStorage.getItem("theme") || "dark" + ); + const [supportEmail, setSupportEmail] = useState("support@shuffler.io"); + const [logoutUrl, setLogoutUrl] = useState(""); useEffect(() => { if (currentLocation?.includes('/workflows/') && leftSideBarOpenByClick === true) { @@ -35,6 +45,57 @@ export const AppContext = (props) => { }; }, []); + const handleThemeChange = (theme) => { + if (!theme || theme === "null" || theme === "undefined") { + localStorage.setItem("theme", "dark"); + setThemeMode("dark"); + return; + } + + const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); + + const applySystemTheme = () => { + const isDark = darkMediaQuery.matches; + setThemeMode(isDark ? "dark" : "light"); + localStorage.setItem("theme", isDark ? "dark" : "light"); + }; + + if (theme === "system") { + applySystemTheme(); + darkMediaQuery.addEventListener("change", applySystemTheme); + return () => { + darkMediaQuery.removeEventListener("change", applySystemTheme); + }; + } else { + setThemeMode(theme); + localStorage.setItem("theme", theme); + } + }; + + + useEffect(() => { + if (serverside === true) return; + + const theme = localStorage.getItem("theme"); + + if (!theme || theme === "null" || theme === "undefined") { + localStorage.setItem("theme", "dark"); + setThemeMode("dark"); + return; + } + + let cleanup; + if (theme === "system") { + cleanup = handleThemeChange("system"); + } else { + handleThemeChange(theme); + } + + return () => { + if (cleanup) cleanup(); + }; + }, []); + return ( { setIsDocSearchModalOpen, searchBarModalOpen, setSearchBarModalOpen, + supportEmail, + setSupportEmail, + logoutUrl, + setLogoutUrl, leftSideBarOpenByClick, setLeftSideBarOpenByClick, - windowWidth + windowWidth, + themeMode, + setThemeMode, + handleThemeChange, + brandColor, + setBrandColor, + brandName, + setBrandName, }}> {props.children} diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index 8a2623f6..79d35e35 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -1,619 +1,626 @@ -const data = [ - { - selector: "node", - css: { - label: function(element) { - var elementname = element.data("label") - if (elementname === null || elementname === undefined) { - return "" - } - - elementname = elementname.replaceAll("_", " ", -1) - elementname = elementname.charAt(0).toUpperCase() + elementname.slice(1) - return elementname - }, - "text-valign": "center", - "text-margin-x": function(element) { - // Attempt at bottom-positioning - // Required text-valign: bottom - // FIXME: Disabled for now. - return "15px" - - - - const name = element.data("label") - console.log("Name: ", name) - if (name === null || name === undefined || name == "" || document=== undefined || document === null) { - return "0px" - } - - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d') - - context.font = '18px Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif' - - const textWidth = context.measureText(name).width - return textWidth + "px" - //return -1*(textWidth) + "px" - }, - - "font-family": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif", - "font-weight": "lighter", - "font-size": "18px", - "margin-right": "10px", - width: "80px", - height: "80px", - color: "white", - padding: "10px", - margin: "5px", - "border-width": "1px", - "z-index": 5001, - }, - }, - { - selector: "edge", - css: { - "target-arrow-shape": "triangle", - "target-arrow-color": "grey", - "curve-style": "unbundled-bezier", - label: "data(label)", - "text-margin-y": "-15px", - width: "2px", - color: "white", - "line-fill": "linear-gradient", - "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["grey", "grey"], - "z-index": 5001, - }, - }, - { - selector: `node[buttonType="ACTIONSUGGESTION"]`, - css: { - label: "data(label)", - shape: "roundrectangle", - "height": "18px", - "width": "145px", - "background-color": "#212121", - "border-color": "#81c784", - "z-index": 10000, - "border-radius": "10px", - "text-margin-x": "0px", - }, - }, - { - selector: `node[type="ACTION"]`, - css: { - shape: "roundrectangle", - "background-color": "#213243", - "border-color": "#81c784", - "background-width": "100%", - "background-height": "100%", - "border-radius": "5px", - "z-index": 5001, - }, - }, - { - selector: `node[type="COMMENT"]`, - css: { - label: function (element) { - return element.data("label") +export default function defaultCytoscapeStyle(theme) { + return [ + { + selector: "node", + css: { + label: function(element) { + var elementname = element.data("label") + if (elementname === null || elementname === undefined) { + return "" + } + + elementname = elementname.replaceAll("_", " ", -1) + elementname = elementname.charAt(0).toUpperCase() + elementname.slice(1) + return elementname + }, + "text-valign": "center", + "text-margin-x": function(element) { + // Attempt at bottom-positioning + // Required text-valign: bottom + // FIXME: Disabled for now. + return "15px" + + + + const name = element.data("label") + console.log("Name: ", name) + if (name === null || name === undefined || name == "" || document=== undefined || document === null) { + return "0px" + } + + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d') + + context.font = '18px Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif' + + const textWidth = context.measureText(name).width + return textWidth + "px" + //return -1*(textWidth) + "px" + }, + + "font-family": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif", + "font-weight": "lighter", + "font-size": "18px", + "margin-right": "10px", + width: "80px", + height: "80px", + color: theme.palette.text.primary || "white", + padding: "10px", + margin: "5px", + "border-width": "1px", + "z-index": 5001, }, - shape: "roundrectangle", - color: "data(color)", - width: "data(width)", - height: "data(height)", - padding: "5px", - margin: "0px", - "background-color": "data(backgroundcolor)", - "background-image": "data(backgroundimage)", - "border-color": "#ffffff", - "text-margin-x": "data(textMarginX)", - "text-margin-y": "data(textMarginY)", - "z-index": 4999, - "border-radius": "5px", - "background-opacity": "0.5", - "text-wrap": "wrap", - "text-max-width": "data(width)", - "text-halign": function(element) { - const align = element?.data("textHalign") - if (align === null || align === undefined || align === "") { - return "center" - } - - return align - }, - "text-valign": function(element) { - const align = element?.data("textValign") - if (align === null || align === undefined || align === "") { - return "center" - } - - return align - } }, - }, - { - selector: `node[type="RESIZE-HANDLE"]`, - css: { - shape: "ellipse", - width: "8px", - height: "8px", - "border-width": 1, - "border-color": "white", - "z-index": 5002, - "overlay-opacity": 0, - "cursor": "nwse-resize", - "opacity": 0, - "pointer-events": "auto", + { + selector: "edge", + css: { + "target-arrow-shape": "triangle", + "target-arrow-color": "grey", + "curve-style": "unbundled-bezier", + label: "data(label)", + "text-margin-y": "-15px", + width: "2px", + color: theme.palette.text.primary || "white", + "line-fill": "linear-gradient", + "line-gradient-stop-positions": ["0.0", "100"], + "line-gradient-stop-colors": ["grey", "grey"], + "z-index": 5001, + }, }, - }, - { - selector: `node[app_name="Integration Framework"]`, - css: { - width: "60px", - height: "60px", - "z-index": 5000, - - //'border-width': 3, - //'border-color': 'transparent', - //'border-style': 'solid', - //'border-gradient': 'linear-gradient(to right, #FF0000, #FF7F00, #FFFF00, #00FF00, #0000FF, #4B0082, #8A2BE2)' - }, - }, - { - selector: `node[example="noapp"]`, - css: { - // Make background image padding on the left side 20px - "background-width": "100%", - "background-height": "100%", - - "background-color": "data(iconBackground)", - "background-fill": "data(fillstyle)", - "background-gradient-direction": "to-bottom-right", - "background-gradient-stop-colors": "data(fillGradient)", - // Change transparency of background - "background-opacity": "0.3", - }, - }, - { - selector: `node[app_name="Shuffle Tools"], node[app_name="email"], node[app_name="http"]`, - css: { - width: "35px", - height: "35px", - "z-index": 5000, - "font-size": "0px", - "background-width": "75%", - "background-height": "75%", - "background-color": "data(iconBackground)", - "background-fill": "data(fillstyle)", - "background-gradient-direction": "to-right", - "background-gradient-stop-colors": "data(fillGradient)", - }, - }, - { - selector: `node[app_id="shuffle_agent"]`, - css: { - "height": "74px", - "width": "222px", - "background-image": "data(large_image)", - "label": function(element) { - var elementname = element.data("label") - if (elementname === null || elementname === undefined) { - return "" - } - - if (elementname.length > 15) { - elementname = elementname.substring(0, 15) + ".." - } - - return elementname - }, - "background-width": "65px", - "background-height": "65px", - "background-position-x": "20px", - //"background-position-x": "center", // Crashes - "background-repeat": "no-repeat", - - "font-size": "14px", - "text-halign": "center", - "text-valign": "center", - "text-margin-x": "-140px", - "text-margin-y": "0px", - - }, - }, - { - selector: `node[app_name="Testing"]`, - css: { - width: "30px", - height: "30px", - "z-index": 5000, - "font-size": "0px", - }, - }, - { - selector: `node[?small_image]`, - css: { - "background-image": "data(small_image)", - "text-halign": "right", - }, - }, - { - selector: `node[?large_image]`, - css: { - "background-image": "data(large_image)", - "text-halign": "right", - }, - }, - { - selector: `node[type="CONDITION"]`, - css: { - shape: "diamond", - "border-color": "##FFEB3B", - padding: "30px", - }, - }, - { - selector: `node[type="eventAction"]`, - css: { - "background-color": "#edbd21", - }, - }, - { - selector: `node[type="TRIGGER"]`, - css: { - shape: "round-octagon", - "border-radius": "5px", - "border-color": "orange", - "background-color": "#213243", - "background-width": "100px", - "background-height": "100px", - }, - }, - { - selector: `node[status="running"]`, - css: { - "border-color": "#81c784", - }, - }, - { - selector: `node[status="stopped"]`, - css: { - "border-color": "orange", - }, - }, - { - selector: 'node[type="mq"]', - css: { - "background-color": "#edbd21", - }, - }, - { - selector: "node[?isButton]", - css: { - shape: "ellipse", - width: "15px", - height: "15px", - "z-index": "5002", - "font-size": "0px", - border: "1px solid rgba(255,255,255,0.9)", - "background-image": "data(icon)", - "background-color": "data(iconBackground)", - }, - }, - { - selector: "node[?isSuggestion]", - css: { - shape: "roundrectangle", - width: "30px", - height: "30px", - "z-index": "5002", - filter: "grayscale(100%)", - border: "1px solid rgba(255,255,255,0.9)", - "background-image": "data(large_image)", - "background-fit": "cover", - "font-size": "20px", - label: "data(label_replaced)", - }, - }, - { - selector: "node[?canConnect]", - css: { - "border-color": "#f86a3e", - "border-width": "10px", - "z-index": "5002", - "background-color": "#f86a3e", - }, - }, - { - selector: "node[?isDescriptor]", - css: { - shape: "ellipse", - "border-color": "#80deea", - width: "5px", - height: "5px", - "z-index": "5002", - "font-size": "10px", - "text-valign": "center", - "text-halign": "center", - border: "1px solid black", - "margin-right": "0px", + { + selector: `node[buttonType="ACTIONSUGGESTION"]`, + css: { + label: "data(label)", + shape: "roundrectangle", + "height": "18px", + "width": "145px", + "background-color": "#212121", + "border-color": "#81c784", + "z-index": 10000, + "border-radius": "10px", "text-margin-x": "0px", - "background-color": "data(imageColor)", - "background-image": "data(image)", - label: "data(label)", + }, }, - }, - { - selector: "node[?isStartNode]", - css: { - shape: function(element) { - return "ellipse" - }, - "border-color": "#80deea", - width: "80px", - height: "80px", - "font-size": "18px", - "background-width": "100%", - "background-height": "100%", + { + selector: `node[type="ACTION"]`, + css: { + shape: "roundrectangle", + "background-color": "#213243", + "border-color": "#81c784", + "background-width": "100%", + "background-height": "100%", + "border-radius": "5px", + "z-index": 5001, + }, }, - }, - { - selector: "node[!is_valid]", - css: { - "border-color": "#f53434", - "border-width": "5px", + { + selector: `node[type="COMMENT"]`, + css: { + label: function (element) { + return element.data("label") + }, + shape: "roundrectangle", + color: "data(color)", + width: "data(width)", + height: "data(height)", + padding: "5px", + margin: "0px", + "background-color": "data(backgroundcolor)", + "background-image": "data(backgroundimage)", + "border-color": "#ffffff", + "text-margin-x": "data(textMarginX)", + "text-margin-y": "data(textMarginY)", + "z-index": 4999, + "border-radius": "5px", + "background-opacity": "0.5", + "text-wrap": "wrap", + "text-max-width": "data(width)", + "text-halign": function(element) { + const align = element?.data("textHalign") + if (align === null || align === undefined || align === "") { + return "center" + } + + return align + }, + "text-valign": function(element) { + const align = element?.data("textValign") + if (align === null || align === undefined || align === "") { + return "center" + } + + return align + } + }, }, - }, - { - selector: ":selected", - css: { - "background-color": "#77b0d0", - "border-color": "#77b0d0", - "border-width": "20px", + { + selector: `node[type="RESIZE-HANDLE"]`, + css: { + shape: "ellipse", + width: "8px", + height: "8px", + "border-width": 1, + "border-color": theme.palette.text.primary || "white", + "z-index": 5002, + "overlay-opacity": 0, + "cursor": "nwse-resize", + "opacity": 0, + "pointer-events": "auto", + }, }, - }, - { - selector: ".skipped-highlight", - css: { - "background-color": "grey", - "border-color": "grey", - "border-width": "8px", - "transition-property": "background-color", - "transition-duration": "0.5s", + { + selector: `node[app_name="Integration Framework"]`, + css: { + width: "60px", + height: "60px", + "z-index": 5000, + + //'border-width': 3, + //'border-color': 'transparent', + //'border-style': 'solid', + //'border-gradient': 'linear-gradient(to right, #FF0000, #FF7F00, #FFFF00, #00FF00, #0000FF, #4B0082, #8A2BE2)' + }, }, - }, - { - selector: ".success-highlight", - css: { - "background-color": "#41dcab", - "border-color": "#41dcab", - "border-width": "5px", - "transition-property": "background-color", - "transition-duration": "0.5s", + { + selector: `node[example="noapp"]`, + css: { + // Make background image padding on the left side 20px + "background-width": "100%", + "background-height": "100%", + + "background-color": "data(iconBackground)", + "background-fill": "data(fillstyle)", + "background-gradient-direction": "to-bottom-right", + "background-gradient-stop-colors": "data(fillGradient)", + // Change transparency of background + "background-opacity": "0.3", + }, }, - }, - { - selector: ".hover-highlight", - css: { - "background-color": "#5f9265", - "border-color": "#5f9265", - "border-width": "5px", - "transition-property": "background-color", - "transition-duration": "0.5s", + { + selector: `node[app_name="Shuffle Tools"], node[app_name="email"], node[app_name="http"]`, + css: { + width: "35px", + height: "35px", + "z-index": 5000, + "font-size": "0px", + "background-width": "75%", + "background-height": "75%", + "background-color": "data(iconBackground)", + "background-fill": "data(fillstyle)", + "background-gradient-direction": "to-right", + "background-gradient-stop-colors": "data(fillGradient)", + }, }, - }, - { - selector: ".failure-highlight", - css: { - "background-color": "#8e3530", - "border-color": "#8e3530", - "border-width": "5px", - "transition-property": "background-color", - "transition-duration": "0.5s", + { + selector: `node[app_name="Testing"]`, + css: { + width: "30px", + height: "30px", + "z-index": 5000, + "font-size": "0px", + }, }, - }, - { - selector: ".not-executing-highlight", - css: { - "background-color": "grey", - "border-color": "grey", - "border-width": "5px", - "transition-property": "#ffef47", - "transition-duration": "0.25s", + { + selector: `node[?small_image]`, + css: { + "background-image": "data(small_image)", + "text-halign": "right", + }, }, - }, - { - selector: ".executing-highlight", - css: { - "background-color": "#ffef47", - "border-color": "#ffef47", - "border-width": "8px", - "transition-property": "border-width", - "transition-duration": "0.25s", + { + selector: `node[?large_image]`, + css: { + "background-image": "data(large_image)", + "text-halign": "right", + }, }, - }, - { - selector: ".awaiting-data-highlight", - css: { - "background-color": "#f4ad42", - "border-color": "#f4ad42", - "border-width": "5px", - "transition-property": "border-color", - "transition-duration": "0.5s", + { + selector: `node[type="CONDITION"]`, + css: { + shape: "diamond", + "border-color": "##FFEB3B", + padding: "30px", + }, }, - }, - { - selector: ".shuffle-hover-highlight", - css: { - "background-color": "#f85a3e", - "border-color": "#f85a3e", - "border-width": "7px", - "transition-property": "border-width", - "transition-duration": "0.25s", + { + selector: `node[type="eventAction"]`, + css: { + "background-color": "#edbd21", + }, + }, + { + selector: `node[type="TRIGGER"]`, + css: { + shape: "round-octagon", + "border-radius": "5px", + "border-color": "orange", + "background-color": "#213243", + "background-width": "100px", + "background-height": "100px", + }, + }, + { + selector: `node[status="running"]`, + css: { + "border-color": "#81c784", + }, + }, + { + selector: `node[status="stopped"]`, + css: { + "border-color": "orange", + }, + }, + { + selector: 'node[type="mq"]', + css: { + "background-color": "#edbd21", + }, + }, + { + selector: "node[?isButton]", + css: { + shape: "ellipse", + width: "15px", + height: "15px", + "z-index": "5002", + "font-size": "0px", + border: "1px solid rgba(255,255,255,0.9)", + "background-image": "data(icon)", + "background-color": "data(iconBackground)", + }, + }, + { + selector: "node[?isSuggestion]", + css: { + shape: "roundrectangle", + width: "30px", + height: "30px", + "z-index": "5002", + filter: "grayscale(100%)", + border: "1px solid rgba(255,255,255,0.9)", + "background-image": "data(large_image)", + "background-fit": "cover", + "font-size": "20px", + label: "data(label_replaced)", + }, + }, + { + selector: "node[?canConnect]", + css: { + "border-color": "#f86a3e", + "border-width": "10px", + "z-index": "5002", + "background-color": "#f86a3e", + }, + }, + { + selector: "node[?isDescriptor]", + css: { + shape: "ellipse", + "border-color": "#80deea", + width: "5px", + height: "5px", + "z-index": "5002", + "font-size": "10px", + "text-valign": "center", + "text-halign": "center", + border: "1px solid black", + "margin-right": "0px", + "text-margin-x": "0px", + "background-color": "data(imageColor)", + "background-image": "data(image)", label: "data(label)", - "font-size": "18px", - color: "white", + }, }, - }, - { - selector: "$node > node", - css: { - "padding-top": "10px", - "padding-left": "10px", - "padding-bottom": "10px", - "padding-right": "10px", + { + selector: "node[?isStartNode]", + css: { + shape: function(element) { + return "ellipse" + }, + "border-color": "#80deea", + width: "80px", + height: "80px", + "font-size": "18px", + "background-width": "100%", + "background-height": "100%", + }, }, - }, - { - selector: "edge.executing-highlight", - css: { - width: "5px", - "target-arrow-color": "#ffef47", - "line-color": "#ffef47", - "transition-property": "line-color, width", - "transition-duration": "0.25s", + { + selector: `node[app_id="shuffle_agent"]`, + css: { + "shape": function(element) { + return "roundrectangle" + }, + "height": "74px", + "width": "222px", + "background-image": "data(large_image)", + "label": function(element) { + var elementname = element.data("label") + if (elementname === null || elementname === undefined) { + return "" + } + + elementname = elementname.replaceAll("_", " ", -1) + + if (elementname.length > 15) { + elementname = elementname.substring(0, 15) + ".." + } + + return elementname + }, + "background-width": "65px", + "background-height": "65px", + "background-position-x": "20px", + "background-repeat": "no-repeat", + "background-opacity": "0.5", + + "font-size": "14px", + "text-halign": "center", + "text-valign": "center", + "text-margin-x": "20px", + "text-margin-y": "0px", + + }, }, - }, - { - selector: `edge[?decorator]`, - css: { - width: "1px", - "line-style": "dashed", - "line-fill": "linear-gradient", - "target-arrow-color": "#555555", - "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["#555555", "#555555"], + { + selector: "node[!is_valid]", + css: { + "border-color": "#f53434", + "border-width": "5px", + }, }, - }, - { - selector: "edge.success-highlight", - css: { - width: "3px", - "target-arrow-color": "#41dcab", - "line-color": "#41dcab", - "transition-property": "line-color, width", - "transition-duration": "0.5s", - "line-fill": "linear-gradient", - "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["#41dcab", "#41dcab"], + { + selector: ":selected", + css: { + "background-color": "#77b0d0", + "border-color": "#77b0d0", + "border-width": "20px", + }, }, - }, - { - selector: ".eh-handle", - style: { - "background-color": "#337ab7", - width: "1px", - height: "1px", - shape: "circle", - "border-width": "1px", - "border-color": "black", + { + selector: ".skipped-highlight", + css: { + "background-color": "grey", + "border-color": "grey", + "border-width": "8px", + "transition-property": "background-color", + "transition-duration": "0.5s", + }, }, - }, - { - selector: ".eh-source", - style: { - "border-width": "3", - "border-color": "#337ab7", + { + selector: ".success-highlight", + css: { + "background-color": "#41dcab", + "border-color": "#41dcab", + "border-width": "5px", + "transition-property": "background-color", + "transition-duration": "0.5s", + }, }, - }, - { - selector: ".eh-target", - style: { - "border-width": "3", - "border-color": "#337ab7", + { + selector: ".hover-highlight", + css: { + "background-color": "#5f9265", + "border-color": "#5f9265", + "border-width": "5px", + "transition-property": "background-color", + "transition-duration": "0.5s", + }, }, - }, - { - selector: ".eh-preview, .eh-ghost-edge", - style: { - "background-color": "#337ab7", - "line-color": "#337ab7", - "target-arrow-color": "#337ab7", - "source-arrow-color": "#337ab7", + { + selector: ".failure-highlight", + css: { + "background-color": "#8e3530", + "border-color": "#8e3530", + "border-width": "5px", + "transition-property": "background-color", + "transition-duration": "0.5s", + }, }, - }, - { - selector: "edge:selected", - css: { - "target-arrow-color": "#f85a3e", + { + selector: ".not-executing-highlight", + css: { + "background-color": "grey", + "border-color": "grey", + "border-width": "5px", + "transition-property": "#ffef47", + "transition-duration": "0.25s", + }, }, - }, - { - selector: `edge[?source_workflow]`, - css: { - "background-opacity": "1", - "font-size": "0px", + { + selector: ".executing-highlight", + css: { + //"background-color": "#ffef47", + "border-color": "#ffef47", + "border-width": "8px", + "transition-property": "border-width", + "transition-duration": "0.25s", + }, }, - }, - { - selector: `node[?source_workflow]`, - css: { - "background-opacity": "0", - "font-size": "0px", + { + selector: ".awaiting-data-highlight", + css: { + "background-color": "#f4ad42", + "border-color": "#f4ad42", + "border-width": "5px", + "transition-property": "border-color", + "transition-duration": "0.5s", + }, }, - }, - { - selector: "node:selected", - css: { - "border-color": "#f86a3e", - "border-width": "7px", - }, - }, - { - selector: `node[buttonType="condition-drag"]`, - css: { - "width": "5px", - "height": "5px", - "background-color": "#f85a3e", + { + selector: ".shuffle-hover-highlight", + css: { + "background-color": "#f85a3e", + "border-color": "#f85a3e", + "border-width": "7px", + "transition-property": "border-width", + "transition-duration": "0.25s", + label: "data(label)", + "font-size": "18px", + color: theme.palette.text.primary || "white", + }, }, - }, - { - selector: `node[name="switch"]`, - css: { - label: function(element) { - // Load from the actual element - var nodeheight = 400 - var conditions = [{ - "name": "Condition 1", - "check": "X equals Y", - }, - { - "name": "Condition 2", - "check": "X2 equals Y2", - }, - { - "name": "Condition 3", - "check": "X3 equals Y3", - }] - - conditions.push({ - "name": "Else", - "check": "If all else fails", - }) - - const newlines = nodeheight / conditions.length - console.log("Newlines: ", newlines) - - const label = conditions.map((condition) => { - return `${condition.name}\n\n\n` - }).join("\n") - - return label - }, - color: "white", - "border-color": "#f85a3e", - "background-color": "#1f1f1f", - "font-size": "19px", - "text-margin-x": "-110px", - "text-wrap": "wrap", - shape: "roundrectangle", - width: "100", - height: "300", - + { + selector: "$node > node", + css: { + "padding-top": "10px", + "padding-left": "10px", + "padding-bottom": "10px", + "padding-right": "10px", + }, }, - }, -]; + { + selector: "edge.executing-highlight", + css: { + width: "5px", + "target-arrow-color": "#ffef47", + "line-color": "#ffef47", + "transition-property": "line-color, width", + "transition-duration": "0.25s", + }, + }, + { + selector: `edge[?decorator]`, + css: { + width: "1px", + "line-style": "dashed", + "line-fill": "linear-gradient", + "target-arrow-color": "#555555", + "line-gradient-stop-positions": ["0.0", "100"], + "line-gradient-stop-colors": ["#555555", "#555555"], + }, + }, + { + selector: "edge.success-highlight", + css: { + width: "3px", + "target-arrow-color": "#41dcab", + "line-color": "#41dcab", + "transition-property": "line-color, width", + "transition-duration": "0.5s", + "line-fill": "linear-gradient", + "line-gradient-stop-positions": ["0.0", "100"], + "line-gradient-stop-colors": ["#41dcab", "#41dcab"], + }, + }, + { + selector: ".eh-handle", + style: { + "background-color": "#337ab7", + width: "1px", + height: "1px", + shape: "circle", + "border-width": "1px", + "border-color": "black", + }, + }, + { + selector: ".eh-source", + style: { + "border-width": "3", + "border-color": "#337ab7", + }, + }, + { + selector: ".eh-target", + style: { + "border-width": "3", + "border-color": "#337ab7", + }, + }, + { + selector: ".eh-preview, .eh-ghost-edge", + style: { + "background-color": "#337ab7", + "line-color": "#337ab7", + "target-arrow-color": "#337ab7", + "source-arrow-color": "#337ab7", + }, + }, + { + selector: "edge:selected", + css: { + "target-arrow-color": "#f85a3e", + }, + }, + { + selector: `edge[?source_workflow]`, + css: { + "background-opacity": "1", + "font-size": "0px", + }, + }, + { + selector: `node[?source_workflow]`, + css: { + "background-opacity": "0", + "font-size": "0px", + }, + }, + { + selector: "node:selected", + css: { + "border-color": "#f86a3e", + "border-width": "7px", + }, + }, + { + selector: `node[buttonType="condition-drag"]`, + css: { + "width": "5px", + "height": "5px", + "background-color": "#f85a3e", + }, + }, + { + selector: `node[name="switch"]`, + css: { + label: function(element) { + // Load from the actual element + var nodeheight = 400 + var conditions = [{ + "name": "Condition 1", + "check": "X equals Y", + }, + { + "name": "Condition 2", + "check": "X2 equals Y2", + }, + { + "name": "Condition 3", + "check": "X3 equals Y3", + }] + + conditions.push({ + "name": "Else", + "check": "If all else fails", + }) + + const newlines = nodeheight / conditions.length + console.log("Newlines: ", newlines) + + const label = conditions.map((condition) => { + return `${condition.name}\n\n\n` + }).join("\n") + + return label + }, + color: theme.palette.text.primary || "white", + "border-color": "#f85a3e", + "background-color": "#1f1f1f", + "font-size": "19px", + "text-margin-x": "-110px", + "text-wrap": "wrap", + shape: "roundrectangle", + width: "100", + height: "300", + + }, + }, + ]; +} //{ // selector: 'edge[?hasErrors]', @@ -626,5 +633,3 @@ const data = [ // "line-gradient-stop-colors": ["#991818", "#991818"], // }, //}, - -export default data; diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index 1621dc42..d7bb0e1e 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -1,4 +1,3 @@ -import React from "react"; import { createTheme, adaptV4Theme } from "@mui/material/styles"; const theme = createTheme(adaptV4Theme({ @@ -153,3 +152,298 @@ const theme = createTheme(adaptV4Theme({ })); export default theme; + +export const getTheme = (themeMode, brandColor) => + createTheme({ + palette: { + mode: themeMode, + main: brandColor || "#FF8544", + primary: { + main: brandColor || "#FF8544", + contrastText: "#ffffff", + }, + secondary: { + main: "rgba(255,255,255,0.7)", + contrastText:"#000000", + }, + text: { + primary: themeMode === "dark" ? "#ffffff" : "#1A1A1A", + secondary: themeMode === "dark" ? "#9E9E9E" : "#616161", + }, + type: themeMode, + inputColor: themeMode === "dark" ? "rgba(39,41,45,1)" : "rgba(245, 245, 245, 1)", + textColor: themeMode === "dark" ? "#F1F1F1" : "#1A1A1A", + textPrimary: themeMode === "dark" ? "rgba(255, 255, 255, 0.8)" : "rgba(26, 26, 26, 0.8)", + surfaceColor: themeMode === "dark" ? "#27292d" : "#EFEFEF", + platformColor: themeMode === "dark" ? "#212121" : "#ffffff", + backgroundColor: themeMode === "dark" ? "#1a1a1a" : "#f1f1f1", + distributionColor: themeMode === "dark" ? "#40E0D0" : "#008080", + cardBackgroundColor: themeMode === "dark" ? "#1e1e1e" : "#eaeaea", + cardHoverColor: themeMode === "dark" ? "#323232" : "#F0F0F0", + hoverColor: themeMode === "dark" ? "#323232" : "#D6D6D6", + green: themeMode === "dark" ? "#5cc879" : "#008000", + defaultBorder: themeMode === "dark" ? '1px solid #494949' : '1px solid #CCCCCC', + linkColor: brandColor === "#ff8544" ? "#f86a3e" : brandColor, + + borderRadius: 10, + loaderColor: themeMode === "dark" ? "#1a1a1a" : "#E0E0E0", + jsonIconStyle: "round", + jsonTheme: themeMode === "dark" ? "summerfruit" : { + base00: "#ffffff", // background + base01: "#f0f0f0", // very light grey + base02: "#f5f5f5", // light grey + base03: "#999999", // dim text + base04: "#444444", // bold keys + base05: "#333333", // normal text + base06: "#1a1a1a", // darker text + base07: "#000000", // black + base08: "#f14c4c", // red + base09: "#f58c1f", // orange + base0A: "#f2c032", // yellow + base0B: "#51975d", // green + base0C: "#2aa198", // teal + base0D: "#007acc", // blue (keys!) + base0E: "#c586c0", // purple + base0F: "#d16969", // brown + }, + jsonCollapseStringsAfterLength: 100, + drawer: { + backgroundColor: themeMode === "dark" ? "#262626" : "#f9f9f9" + }, + reactJsonStyle: { + padding: 5, + width: "98%", + borderRadius: 5, + border: themeMode === "dark" ? "1px solid rgba(255,255,255,0.7)" : "1px solid rgba(0,0,0,0.3)", + backgroundColor: themeMode === "dark" + ? "#1A1A1A" + : "#f1f1f1", + color: themeMode === "dark" + ? "#F1F1F1" + : "#1A1A1A", + overflowX: "auto", + }, + textFieldStyle: { + backgroundColor: themeMode === "dark" ? "#212121" : "#FFFFFF", + color: themeMode === "dark" ? "#ffffff" : "#000000", + borderRadius: "5px", + height: 40, + border: themeMode === "dark" ? "1px solid #4D4D4D" : "1px solid #E0E0E0", + }, + DialogStyle: { + backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff", + borderRadius: 2, + boxShadow: themeMode === "dark" ? "0px 0px 10px 0px rgba(0,0,0,0.75)" : "0px 0px 10px 0px rgba(0,0,0,0.2)", + border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc", + }, + innerTextfieldStyle: { + height: 40, + fontSize: 16, + backgroundColor: themeMode === "dark" ? "#212121" : "#f5f5f5", + }, + tooltip: { + backgroundColor: themeMode === "dark" ? "#212121" : "#ffffff", + color: themeMode === "dark" ? "#ffffff" : "#000000", + border: themeMode === "dark" ? "1px solid #494949" : "1px solid #cccccc", + }, + chipStyle: { + backgroundColor: themeMode === "dark" ? "#333333" : "#F5F5F5", + borderColor: themeMode === "dark" ? "#444444" : "#E0E0E0", + color: themeMode === "dark" ? "#FFFFFF" : "#333333", + }, + defaultImage: "/images/no_image.png", + singulOrange: "/images/singul_orange.png", + singulGreen: "/images/singul_green.png", + singulBlackWhite: "/images/singul_black_white.png", + scrollbarColor: themeMode === "dark" ? "#494949 #2f2f2f": "#c1c1c1 #f1f1f1", + scrollbarColorTransparent: themeMode === "dark" ? '#494949 transparent': "#c1c1c1 transparent", + }, + typography: { + fontFamily: `"inter", "Roboto", "Helvetica", "Arial", sans-serif`, + color: themeMode === "dark" ? "#ffffff" : "#000000", + useNextVariants: true, + fontWeightLight: 300, + fontWeightRegular: 400, + fontWeightMedium: 500, + fontWeightSemiBold: 600, + fontWeightBold: 700, + allVariants: { + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A", + }, + h1: { + fontSize: 40, + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A" + }, + h2: { + fontSize: 36, + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A" + }, + h3: { + fontSize: 32, + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A" + }, + h4: { + fontSize: 30, + fontWeight: 500, + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A" + }, + h6: { + fontSize: 22, + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A" + }, + body1: { + fontSize: 16, + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A" + }, + body2: { + fontSize: 14, + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A" + }, + }, + components: { + MuiButton: { + styleOverrides: { + root: { + textTransform: 'none', + borderRadius: '4px', + }, + }, + variants: [ + { + props: { variant: 'text', color: 'primary' }, + style: { + color: themeMode === "dark" ? "#ffffff" : "#1A1A1A", + whiteSpace: "nowrap", + textWrap: "normal", + }, + }, + { + props: { variant: 'text', color: 'secondary' }, + style: { + color: themeMode === "dark" ? "#9E9E9E" : "#616161", + whiteSpace: "nowrap", + textWrap: "normal", + }, + }, + { + props: { variant: 'contained', color: 'primary' }, + style: { + backgroundColor: themeMode === "dark" ? brandColor || '#ff8544' : brandColor || '#FF7C35', + color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF', + borderRadius: '4px', + whiteSpace: "nowrap", + textWrap: "normal", + '&:hover': { + fontWeight: 600, + backgroundColor: themeMode === 'dark' ? brandColor || "#ff955c" : brandColor || '#FF8D4F', + color: themeMode === "dark" ? '#1a1a1a': '#FFFFFF', + }, + }, + }, + { + props: { variant: 'contained', color: 'secondary' }, + style: { + backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9', + color: themeMode === "dark" ? '#ffffff' : '#4C4C4C', + borderRadius: '4px', + boxShadow: 'none', + whiteSpace: "nowrap", + textWrap: "normal", + '&:hover': { + fontWeight: 600, + border: themeMode === "dark" ? '1px solid #f1f1f1' : 'none', + backgroundColor: themeMode === "dark" ? '#494949' : '#C9C9C9', + color: themeMode === "dark" ? '#ffffff' : '#4C4C4C', + }, + }, + }, + { + props: { variant: 'outlined', color: 'primary' }, + style: { + borderColor: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f", + color: themeMode === "dark" ? brandColor || "#ff8544" : brandColor || "#cc5f1f", + whiteSpace: "nowrap", + fontWeight: 'normal', + textWrap: "normal", + '&:hover': { + backgroundColor: themeMode === "dark" ? brandColor || "#ff8544" : "#ffe8dc", + color: themeMode === "dark" ? "#1a1a1a" : "#8a3d00", + fontWeight: 600, + }, + }, + }, + { + props: { variant: 'outlined', color: 'secondary' }, + style: { + border: '1px solid #C5C5C5', + color: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D', + whiteSpace: "nowrap", + textWrap: "normal", + '&:hover': { + backgroundColor: themeMode === "dark" ? '#C5C5C5' : '#EFEFEF', + borderColor: themeMode === "dark" ? '#C5C5C5' : '#2D2D2D', + fontWeight: 600, + color: themeMode === "dark" ? '#1a1a1a' : '#1A1A1A', + }, + }, + }, + ], + }, + MuiTab: { + styleOverrides: { + root: { + color: themeMode === "dark" ? "#C5C5C5" : "#1A1A1A", + }, + }, + }, + }, + + overrides: { + MuiMenu: { + list: { + backgroundColor: themeMode === "dark" ? "#27292d" : "#ffffff", + }, + }, + MuiCssBaseline: { + MuiCssBaseline: { + styleOverrides: ` + @font-face { + font-family: 'Roboto'; + font-style: normal; + font-display: swap; + font-weight: 300; + src: local('Roboto Light'), local('Roboto-Light'); + } + @font-face { + font-family: 'Roboto'; + font-style: normal; + font-display: swap; + font-weight: 400; + src: local('Roboto'), local('Roboto-Regular'); + } + @font-face { + font-family: 'Roboto'; + font-style: normal; + font-display: swap; + font-weight: 500; + src: local('Roboto Medium'), local('Roboto-Medium'); + } + @font-face { + font-family: 'Roboto'; + font-style: normal; + font-display: swap; + font-weight: 600; + src: local('Roboto SemiBold'), local('Roboto-SemiBold'); + } + @font-face { + font-family: 'Roboto'; + font-style: normal; + font-display: swap; + font-weight: 700; + src: local('Roboto Bold'), local('Roboto-Bold'); + } + `, + }, + }, + }, + }); diff --git a/frontend/src/views/Admin2.jsx b/frontend/src/views/Admin2.jsx index 30a356e5..aaff1429 100644 --- a/frontend/src/views/Admin2.jsx +++ b/frontend/src/views/Admin2.jsx @@ -1,6 +1,7 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import AdminNavBar from '../components/AdminNavBar.jsx'; import { toast } from "react-toastify"; +import { Context } from '../context/ContextApi.jsx'; const Admin2 = (props) => { // Destructure props if needed @@ -10,13 +11,15 @@ const Admin2 = (props) => { const [selectedOrganization, setSelectedOrganization] = useState({}); const [organizationFeatures, setOrganizationFeatures] = useState({}); const [orgRequest, setOrgRequest] = React.useState(true); + const [isOrgLoaded, setIsOrgLoaded] = React.useState(false); + const {brandName} = useContext(Context) const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; if (document !== undefined) { - if (selectedOrganization?.name !== undefined) { - document.title = selectedOrganization?.name + " - Admin - Shuffle" + if (selectedOrganization?.name !== undefined) { + document.title = brandName?.length > 0 ? selectedOrganization?.name + ` - Admin - ${brandName}` : selectedOrganization?.name + ` - Admin - Shuffle`; } else { - document.title = "Admin - Shuffle" + document.title = brandName?.length > 0 ? `Admin - ${brandName}` : `Admin - Shuffle`; } } @@ -151,6 +154,8 @@ const Admin2 = (props) => { .catch((error) => { console.log("Error getting org: ", error); toast("Error getting current organization"); + }).finally(() => { + setIsOrgLoaded(true) }); }; @@ -330,7 +335,7 @@ const Admin2 = (props) => { return (
- +
); }; diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 64d594bf..fba1d24a 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useLayoutEffect, memo, useMemo, useRef, useContext } from "react"; import ReactDOM from "react-dom" -import theme from "../theme.jsx"; +import { getTheme } from "../theme.jsx"; import { useInterval } from "react-powerhooks"; import { makeStyles, } from "@mui/styles"; @@ -143,11 +143,11 @@ import edgehandles from "cytoscape-edgehandles"; import CytoscapeComponent from "react-cytoscapejs"; import Draggable from "react-draggable"; -import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; +import defaultCytoscapeStyle from "../defaultCytoscapeStyle.jsx"; import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx" -import { validateJson, collapseField, GetIconInfo } from "../views/Workflows.jsx"; +import { validateJson, collapseField, GetIconInfo, handleReactJsonClipboard, HandleJsonCopy, } from "../views/Workflows.jsx"; import { GetParsedPaths, internalIds, } from "../views/Apps.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; @@ -164,6 +164,33 @@ export const triggers = [ { name: "Webhook", type: "TRIGGER", + parameters : [ + { + "name": "url", + "example": "", + "value": "" + }, + { + "name": "tmp", + "example": "", + "value": "", + }, + { + "name": "auth_headers", + "example": "", + "value": "", + }, + { + "name": "custom_response_body", + "example": "", + "value": "", + }, + { + "name": "await_response", + "example": "", + "value": "v1", + }, + ], status: "uninitialized", trigger_type: "WEBHOOK", errors: null, @@ -179,6 +206,18 @@ export const triggers = [ { name: "Schedule", type: "TRIGGER", + parameters : [ + { + "name": "cron", + "example": "", + "value": "*/25 * * * *" + }, + { + "name": "execution_argument", + "example": "", + "value": "", + }, + ], status: "uninitialized", trigger_type: "SCHEDULE", errors: null, @@ -223,6 +262,38 @@ export const triggers = [ { name: "User Input", type: "TRIGGER", + parameters: [ + { + "name": "alertinfo", + "example": "", + "value": "Do you want to continue the workflow? Start parameters: $exec", + }, + { + "name": "options", + "example": "", + "value": "boolean", + }, + { + "name": "type", + "example": "", + "value": "subflow", + }, + { + "name": "email", + "example": "", + "value": "test@test.com", + }, + { + "name": "sms", + "example": "", + "value": "0000000", + }, + { + "name": "subflow", + "example": "", + "value": "", + } + ], status: "running", large_image: "/images/workflows/UserInput2.svg", description: "Wait for user input trigger", @@ -324,6 +395,7 @@ export function SetJsonDotnotation(jsonInput, inputKey) { return jsonInput; } + //export const green = "#86c142"; export const green = "#02CB70" export const yellow = "#FECC00"; @@ -359,31 +431,6 @@ export function removeParam(key, sourceURL) { return rtn; } -const useStyles = makeStyles({ - notchedOutline: { - borderColor: "#FF8544 !important", - }, - root: { - "& .MuiAutocomplete-listbox": { - border: "2px solid #FF8544", - color: "white", - fontSize: 18, - "& li:nth-child(even)": { - backgroundColor: "#CCC", - }, - "& li:nth-child(odd)": { - backgroundColor: "#FFF", - }, - }, - }, - inputRoot: { - color: "white", - "&:hover .MuiOutlinedInput-notchedOutline": { - borderColor: "#f86a3e", - }, - }, -}); - const splitter = "|~|"; const svgSize = 24; const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); @@ -394,6 +441,8 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const AngularWorkflow = (defaultprops) => { const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id, ReactGA, } = defaultprops; + const {themeMode, supportEmail, brandColor} = useContext(Context) + const theme = getTheme(themeMode, brandColor) const referenceUrl = globalUrl + "/api/v1/hooks/"; //const alert = useAlert() let navigate = useNavigate(); @@ -407,10 +456,36 @@ const AngularWorkflow = (defaultprops) => { var to_be_copied = ""; const [firstrequest, setFirstrequest] = React.useState(true); - const [cystyle] = useState(cytoscapestyle); + const cystyle = useMemo(() => defaultCytoscapeStyle(theme), [themeMode]); + // const cystyle = useMemo(() => defaultCytoscapeStyle, [themeMode]); const [cy, setCy] = React.useState(); + const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#FF8544 !important", + }, + root: { + "& .MuiAutocomplete-listbox": { + border: "2px solid #FF8544", + color: theme.palette.text.primary, + fontSize: 18, + "& li:nth-child(even)": { + backgroundColor: "#CCC", + }, + "& li:nth-child(odd)": { + backgroundColor: "#FFF", + }, + }, + }, + inputRoot: { + color: theme.palette.text.primary, + "&:hover .MuiOutlinedInput-notchedOutline": { + borderColor: "#f86a3e", + }, + }, + }); + const [toolsApp, setToolsApp] = React.useState({}); const [currentView, setCurrentView] = React.useState(0); const [triggerAuthentication, setTriggerAuthentication] = React.useState({}); @@ -434,6 +509,7 @@ const AngularWorkflow = (defaultprops) => { "default", ] }); + const [appGroup, setAppGroup] = React.useState([]); const [triggerGroup, setTriggerGroup] = React.useState([]); const [executionText, setExecutionText] = React.useState(""); @@ -649,6 +725,7 @@ const AngularWorkflow = (defaultprops) => { "Nothing", "Create ticket", "List tickets", + "Send Email", "Get specific ticket", ], "multiselect": true, @@ -704,6 +781,10 @@ const AngularWorkflow = (defaultprops) => { "list_tickets", "get_ticket", "create_ticket", + "close_ticket", + "add_comment", + "update_ticket", + "search_tickets" ], "required": true, }, @@ -733,7 +814,13 @@ const AngularWorkflow = (defaultprops) => { "value": "list_messages", "options": [ "list_messages", + "get_message", "send_message", + "search_messages", + "list_attachments", + "get_attachment", + "create_contact", + "get_contact" ], "required": true, }, @@ -750,9 +837,38 @@ const AngularWorkflow = (defaultprops) => { "label": "IAM", "parameters": [{ "name": "action", - "value": "get_kms_key", + "value": "get_asset", + "options": [ + "reset_password", + "enable_user", + "disable_user", + "get_identity", + "get_asset", + "search_identity" + ], + "required": true, + }, + { + "name": "fields", + "value": "", + "required": false, + "multiline": true, + }] + }, + { + "name": "Assets", + "description": "Available actions for Assets", + "label": "Assets", + "parameters": [{ + "name": "action", + "value": "search_vulnerabilities", "options": [ - "get_kms_key", + "list_assets", + "get_asset", + "search_assets", + "search_users", + "search_endpoints", + "search_vulnerabilities" ], "required": true, }, @@ -763,6 +879,104 @@ const AngularWorkflow = (defaultprops) => { "multiline": true, }] }, + { + "name": "Eradication", + "description": "Available actions for Eradication", + "label": "Eradication", + "parameters": [{ + "name": "action", + "value": "list_alerts", + "options": [ + "list_alerts", + "close_alert", + "get_alert", + "create_detection", + "block_hash", + "search_hosts", + "isolate_host", + "unisolate_host", + "trigger_host_scan" + ], + "required": true, + }, + { + "name": "fields", + "value": "", + "required": false, + "multiline": true, + }] + }, + { + "name": "Intel", + "description": "Available actions for Intel", + "label": "Intel", + "parameters": [{ + "name": "action", + "value": "get_ioc", + "options": [ + "get_ioc", + "search_ioc", + "create_ioc", + "update_ioc", + "delete_ioc" + ], + "required": true, + }, + { + "name": "fields", + "value": "", + "required": false, + "multiline": true, + }] + }, + { + "name": "Network", + "description": "Available actions for Network", + "label": "Network", + "parameters": [{ + "name": "action", + "value": "get_rules", + "options": [ + "get_rules", + "allow_ip", + "block_ip" + ], + "required": true, + }, + { + "name": "fields", + "value": "", + "required": false, + "multiline": true, + }] + }, + { + "name": "SIEM", + "description": "Available actions for SIEM", + "label": "SIEM", + "parameters": [{ + "name": "action", + "value": "search", + "options": [ + "search", + "list_alerts", + "close_alert", + "get_alert", + "create_detection", + "add_to_lookup_list", + "isolate_endpoint" + ], + "required": true, + }, + { + "name": "fields", + "value": "", + "required": false, + "multiline": true, + }] + }, + + ] }] @@ -818,6 +1032,14 @@ const AngularWorkflow = (defaultprops) => { return } + // Ensures reloads don't randomly happen + const appsearchValue = document.getElementById("appsearch") + if (appsearchValue !== undefined && appsearchValue !== null) { + if (appsearchValue.value !== undefined && appsearchValue.value !== null && appsearchValue.value.length > 0) { + return + } + } + if (loadedApps.includes(appId)) { //console.log("App already loaded: ", appId) @@ -2136,7 +2358,6 @@ const AngularWorkflow = (defaultprops) => { } // Controls the colors and direction of execution results. - // Style is in defaultCytoscapeStyle.js const handleUpdateResults = (responseJson, executionRequest) => { if (responseJson === undefined || responseJson === null || responseJson.success === false) { stop() @@ -2659,7 +2880,7 @@ const AngularWorkflow = (defaultprops) => { if (responseJson.reason !== undefined && responseJson.reason !== null) { toast("Failed to save: " + responseJson.reason); } else { - toast("Failed to save. Please contact your support@shuffler.io or your local admin if this is unexpected.") + toast(`Failed to save. Please contact your ${supportEmail} or your local admin if this is unexpected.`) } } else { setSavingState(1); @@ -2845,7 +3066,7 @@ const AngularWorkflow = (defaultprops) => { const runFromHere = (curAction) => { if (curAction.app_id === undefined || curAction.app_id === null || curAction.app_id.length === 0) { - toast.error("No app id found for action. Please contact support@shuffler.io if this persists") + toast.error(`No app id found for action. Please contact ${supportEmail} if this persists`) return } @@ -2904,7 +3125,7 @@ const AngularWorkflow = (defaultprops) => { if (responseJson?.reason !== undefined && responseJson?.reason !== null && responseJson?.reason.length > 0) { toast.error(responseJson.reason) } else { - toast.error("Failed to run the action. Please try again or contact support@shuffler.io") + toast.error(`Failed to run the action. Please try again or contact ${supportEmail}`) } return @@ -3595,6 +3816,7 @@ const AngularWorkflow = (defaultprops) => { "border-opacity": ".7", "font-size": "25px", "border-color": color, + "color": theme.palette.text.primary } const animationDuration = 150 @@ -4352,11 +4574,11 @@ const AngularWorkflow = (defaultprops) => { if (execFound === null && sessionToken === null) { if (isCloud) { - toast.error(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds. If you recently deleted this workflow, speak with support@shuffler.io to recover it from a revision.`, { + toast.error(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds. If you recently deleted this workflow, speak with ${supportEmail} to recover it from a revision.`, { autoClose: 10000, }) } else { - toast.error(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds. Contact support@shuffler.io if this is unexpected.`, { + toast.error(`You don't have access to this workflow or loading failed. Redirecting to workflows in a few seconds. Contact ${supportEmail} if this is unexpected.`, { autoClose: 10000, }) } @@ -8642,9 +8864,9 @@ const AngularWorkflow = (defaultprops) => { } // Set the edge to be dashed - edge.style("target-arrow-color", "white") + edge.style("target-arrow-color", theme.palette.text.primary) edge.style("line-style", "solid") - edge.style("line-gradient-stop-colors", ["white", "white"]) + edge.style("line-gradient-stop-colors", [theme.palette.text.primary, theme.palette.text.primary]) } } @@ -8788,7 +9010,7 @@ const AngularWorkflow = (defaultprops) => { } if (nodedata.type !== "COMMENT") { - parsedStyle.color = "white"; + parsedStyle.color = theme.palette.text.primary; } if (event.target !== undefined && event.target !== null) { @@ -8847,9 +9069,9 @@ const AngularWorkflow = (defaultprops) => { const edgeData = event.target.data(); if (edgeData.decorator === true) { // Set color of it to white and not stripled - event.target.style("target-arrow-color", "white") + event.target.style("target-arrow-color", theme.palette.text.primary) event.target.style("line-style", "solid") - event.target.style("line-gradient-stop-colors", ["white", "white"]) + event.target.style("line-gradient-stop-colors", [theme.palette.text.primary, theme.palette.text.primary]) return; } @@ -10200,7 +10422,7 @@ const AngularWorkflow = (defaultprops) => { minWidth: isMobile ? 50 : "100%", maxWidth: isMobile ? 50 : "100%", marginTop: "5px", - color: "white", + color: theme.palette.text.primary, backgroundColor: theme.palette.surfaceColor, cursor: "pointer", display: "flex", @@ -10213,7 +10435,7 @@ const AngularWorkflow = (defaultprops) => { minWidth: "100%", maxWidth: "100%", marginTop: "5px", - color: "white", + color: theme.palette.text.primary, backgroundColor: theme.palette.surfaceColor, cursor: "pointer", display: "flex", @@ -10305,7 +10527,7 @@ const AngularWorkflow = (defaultprops) => { aria-controls="long-menu" aria-haspopup="true" onClick={menuClick} - style={{ color: "white" }} + style={{ color: theme.palette.text.primary }} > @@ -10327,7 +10549,7 @@ const AngularWorkflow = (defaultprops) => { { setOpen(false); @@ -10353,7 +10575,7 @@ const AngularWorkflow = (defaultprops) => { { deleteVariable(type, index); @@ -10389,7 +10611,7 @@ const AngularWorkflow = (defaultprops) => { rel="noopener noreferrer" href="https://shuffler.io/docs/workflows#workflow_variables" target="_blank" - style={{ textDecoration: "none", color: "#FF8544" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > Workflow variables? @@ -10439,7 +10661,7 @@ const AngularWorkflow = (defaultprops) => { rel="noopener noreferrer" href="https://shuffler.io/docs/workflows#execution_variables" target="_blank" - style={{ textDecoration: "none", color: "#FF8544" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > Runtime variables? @@ -10754,6 +10976,7 @@ const AngularWorkflow = (defaultprops) => { large_image: data.large_image, status: "uninitialized", name: data.name, + parameters: data?.parameters, isStartNode: false, position: newposition, } @@ -10955,6 +11178,10 @@ const AngularWorkflow = (defaultprops) => { } const handleAppDrag = (e, app) => { + if (cy === undefined || cy === null) { + return + } + const cycontainer = cy.container() // Handling drag of public apps @@ -11482,11 +11709,11 @@ const AngularWorkflow = (defaultprops) => { 17 ? -3 : 8, + color: theme.palette.textPrimary }} > {newAppname} @@ -11650,7 +11877,7 @@ const AngularWorkflow = (defaultprops) => { var type = "app" const baseImage = return ( -
+
{hits.length === 0 ? @@ -11671,7 +11898,7 @@ const AngularWorkflow = (defaultprops) => { overflowX: "hidden", overflowY: "hidden", borderBottom: "1px solid rgba(255,255,255,0.4)", - backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit", + backgroundColor: mouseHoverIndex === index ? theme.palette.hoverColor : "inherit", cursor: "pointer", marginLeft: 0, marginRight: 0, @@ -11744,7 +11971,7 @@ const AngularWorkflow = (defaultprops) => { }} defaultPosition={{ x: 0, y: 0 }} > -
{ +
{ clickedApp(hit) }}> @@ -11791,8 +12018,7 @@ const AngularWorkflow = (defaultprops) => {
{title} @@ -11882,7 +12108,7 @@ const AngularWorkflow = (defaultprops) => { )} {visibleApps.length > extraApps.length ? - + Your Apps : null} @@ -12049,7 +12275,7 @@ const AngularWorkflow = (defaultprops) => { const newaction = selectedApp.actions.find((a) => a.name === e.target.value) if (newaction === undefined || newaction === null) { - toast("Failed to find the action you selected. Please try again or contact support@shuffler.io if it persists."); + toast(`Failed to find the action you selected. Please try again or contact ${supportEmail} if it persists.`); return; } @@ -12306,6 +12532,8 @@ const AngularWorkflow = (defaultprops) => { const selectedTriggerChange = (event) => { selectedTrigger.label = event.target.value; setSelectedTrigger(selectedTrigger); + workflow.triggers[selectedTriggerIndex].label = event.target.value; + setWorkflow(workflow); }; // Starts on current node and climbs UP the tree to the root object. @@ -12395,8 +12623,8 @@ const AngularWorkflow = (defaultprops) => { const appApiViewStyle = { display: "flex", flexDirection: "column", - backgroundColor: "#1F2023", - color: "white", + backgroundColor: theme.palette.DialogStyle.backgroundColor, + color: theme.palette.text.primary, paddingRight: 15, paddingLeft: 15, minHeight: "100%", @@ -12475,16 +12703,19 @@ const AngularWorkflow = (defaultprops) => { }; const setTriggerCronWrapper = (value) => { - console.log("Cron Value: ", value) if (selectedTrigger.parameters === null) { selectedTrigger.parameters = []; } - + selectedTrigger.parameters[0] = { + value: value, + name: "cron", + }; workflow.triggers[selectedTriggerIndex].parameters[0] = { value: value, name: "cron", }; setWorkflow(workflow); + setSelectedTrigger(selectedTrigger); }; const setTriggerOptionsWrapper = (value) => { @@ -12530,11 +12761,14 @@ const AngularWorkflow = (defaultprops) => { if (selectedTrigger.parameters === null) { selectedTrigger.parameters = []; } - + selectedTrigger.parameters[0] = { + value : value + } workflow.triggers[selectedTriggerIndex].parameters[0] = { value: value, name: "alertinfo", }; + setSelectedTrigger(selectedTrigger) setWorkflow(workflow); }; @@ -12798,7 +13032,7 @@ const AngularWorkflow = (defaultprops) => { > @@ -12815,7 +13049,7 @@ const AngularWorkflow = (defaultprops) => { data.value !== undefined && data.value !== null && data.value.includes(".#") ? ( - + Use "Shuffle Tools" app with "Filter List" action to handle loops ) : null @@ -12966,7 +13200,7 @@ const AngularWorkflow = (defaultprops) => { startIcon={} sx={{ marginLeft: 10, - color: "white", + color: theme.palette.text.primary, fontSize: "15px", fontFamily: theme?.typography?.fontFamily, textTransform: "none", @@ -12978,6 +13212,7 @@ const AngularWorkflow = (defaultprops) => { justifyContent: "flex-start", "&:hover": { backgroundColor: "transparent", + color: theme.palette.text.primary, border: "none" }, // Disable ripple effect @@ -12987,7 +13222,7 @@ const AngularWorkflow = (defaultprops) => { }} > - Auto Complete + Auto Complete { onClose={handleMenuClose} PaperProps={{ style: { - backgroundColor: "#82ccc3", - color: "white", + backgroundColor: theme.palette.backgroundColor, + color: theme.palette.text.primary, marginTop: 2, maxHeight: 400, }, @@ -13121,7 +13356,7 @@ const AngularWorkflow = (defaultprops) => { } parentMenuOpen={!!menuPosition} style={{ - color: "white", + color: theme.palette.text.primary, minWidth: 250, maxWidth: 250, maxHeight: 50, @@ -13140,7 +13375,7 @@ const AngularWorkflow = (defaultprops) => { style={{ // backgroundColor: theme.palette.inputColor, marginLeft: 15, - color: "white", + color: theme.palette.text.primary, minWidth: 250, maxWidth: 250, padding: 0, @@ -13183,7 +13418,7 @@ const AngularWorkflow = (defaultprops) => { key={pathdata.name} style={{ // backgroundColor: theme.palette.inputColor, - color: "white", + color: theme.palette.text.primary, minWidth: 250, maxWidth: 250, padding: boxPadding, @@ -13224,7 +13459,7 @@ const AngularWorkflow = (defaultprops) => { key={innerdata.name} style={{ // backgroundColor: theme.palette.inputColor, - color: "white", + color: theme.palette.text.primary, padding: "10px 12px", // Add padding here }} value={innerdata} @@ -13257,7 +13492,7 @@ const AngularWorkflow = (defaultprops) => { }; const menuItemStyle = { - color: "white", + color: theme.palette.text.primary, backgroundColor: theme.palette.inputColor, }; @@ -13321,7 +13556,7 @@ const AngularWorkflow = (defaultprops) => { style: { padding: 30, pointerEvents: "auto", - color: "white", + color: theme.palette.text.primary, minWidth: isMobile ? "90%" : 800, border: theme.palette.defaultBorder, @@ -13344,11 +13579,11 @@ const AngularWorkflow = (defaultprops) => { setAuthgroupModalOpen(false) }} > - + - Authgroup Selection + Authgroup Selection @@ -13445,7 +13680,7 @@ const AngularWorkflow = (defaultprops) => { style: { padding: 30, pointerEvents: "auto", - color: "white", + color: theme.palette.text.primary, minWidth: isMobile ? "90%" : 650, border: theme.palette.defaultBorder, @@ -13468,11 +13703,11 @@ const AngularWorkflow = (defaultprops) => { setExecutionArgumentModalOpen(false) }} > - + - Provide an execution argument + Provide an execution argument @@ -13491,7 +13726,7 @@ const AngularWorkflow = (defaultprops) => { InputProps={{ style: { height: "50px", - color: "white", + color: theme.palette.text.primary, fontSize: "1em", }, }} @@ -13656,7 +13891,7 @@ const AngularWorkflow = (defaultprops) => { open={aiQueryModalOpen} PaperProps={{ style: { - color: "white", + color: theme.palette.text.primary, minWidth: isMobile ? "90%" : 450, border: theme.palette.defaultBorder, padding: 50, @@ -13686,7 +13921,7 @@ const AngularWorkflow = (defaultprops) => { onClick={(e) => { }} > - + { position: "absolute", top: 6, right: 6, - color: "white", + color: theme.palette.text.primary, }} onClick={() => { setAiQueryModalOpen(false) @@ -13706,7 +13941,7 @@ const AngularWorkflow = (defaultprops) => { Shuffle AI - What you write here will be fed to the Shuffle AI to generate a change for the selected action or field. Best used for when you are stuck with formatting. Uses your AI credits (resets monthly). Beta feature. Please give feedback to support@shuffler.io {"<"}3 + What you write here will be fed to the Shuffle AI to generate a change for the selected action or field. Best used for when you are stuck with formatting. Uses your AI credits (resets monthly). Beta feature. Please give feedback to {supportEmail} {"<"}3 { aria-labelledby="draggable-dialog-title" open={conditionsModalOpen} PaperProps={{ - style: { + sx: { pointerEvents: "auto", - color: "white", - minWidth: isMobile ? "90%" : 800, + color: theme.palette.DialogStyle.color, + minWidth: isMobile ? "90%" : "800px", border: theme.palette.defaultBorder, - borderRadius: theme.palette.borderRadius, - backgroundColor: "black", + borderRadius: theme.palette.DialogStyle.borderRadius, + backgroundColor: theme.palette.DialogStyle.backgroundColor, + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogActions-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, }, }} onClose={() => { @@ -13798,7 +14042,8 @@ const AngularWorkflow = (defaultprops) => { position: "absolute", bottom: 10, left: 10, - color: "rgba(255,255,255,0.6)", + color: theme.palette.textColor, + backgroundColor: 'inherit', zIndex: 10000, }} > @@ -13809,7 +14054,7 @@ const AngularWorkflow = (defaultprops) => { href="/docs/workflows#conditions" style={{ textDecoration: "none", - color: "#FF8544", + color: theme.palette.linkColor, }} > Learn more @@ -13817,7 +14062,7 @@ const AngularWorkflow = (defaultprops) => { - Condition + Condition
@@ -14027,7 +14272,7 @@ const AngularWorkflow = (defaultprops) => {
- +
) : null} @@ -21559,7 +21669,7 @@ const AngularWorkflow = (defaultprops) => { {foundnotifications > 0 ? { e.preventDefault() e.stopPropagation() @@ -21593,19 +21703,24 @@ const AngularWorkflow = (defaultprops) => {
) : ( -
- +
+ No executions found for the '{executionFilter}' filter.
) : ( -
+
{ stop() }} > - +

{ const newitem = removeParam("execution_id", cursearch); navigate(curpath + newitem) @@ -21665,12 +21780,12 @@ const AngularWorkflow = (defaultprops) => { -
+

Details

{ @@ -21752,7 +21867,7 @@ const AngularWorkflow = (defaultprops) => { @@ -21789,7 +21904,7 @@ const AngularWorkflow = (defaultprops) => {

+ + {data.action.app_name === "AI Agent" || data.action.app_name === "Shuffle Agent" ? + + + { }} + > + + + + + : null} + {data.action.app_name === "shuffle-subflow" && validate.result.success !== undefined && validate.result.success === true ? ( @@ -22432,7 +22571,7 @@ const AngularWorkflow = (defaultprops) => { target="_blank" style={{ textDecoration: "none", - color: "#FF8544", + color: theme.palette.linkColor, }} onClick={(event) => { }} > @@ -22564,7 +22703,7 @@ const AngularWorkflow = (defaultprops) => { Action Logs - More log details for this action are not available without an onprem environment with the SHUFFLE_LOGS_DISABLED environment variable set to false: SHUFFLE_LOGS_DISABLED=false. Logs are enabled by default, except in scale mode. + More log details for this action are not available without an onprem environment with the SHUFFLE_LOGS_DISABLED environment variable set to false: SHUFFLE_LOGS_DISABLED=false. Logs are enabled by default, except in scale mode.
) @@ -22644,7 +22783,7 @@ const AngularWorkflow = (defaultprops) => { variant="body2" style={{ whiteSpace: 'pre-line', - color: showlink ? "#FF8544" : "white", + color: showlink ? "#FF8544" : theme.palette.text.primary, cursor: showlink ? "pointer" : "default", }} onClick={(e) => { @@ -22699,7 +22838,7 @@ const AngularWorkflow = (defaultprops) => { } if (result.status === 405) { - return "Method not allowed. Check the URL to ensure it has all the required parameters. If you keep getting a 405, please forward a screenshot of this to support@shuffler.io" + return `Method not allowed. Check the URL to ensure it has all the required parameters. If you keep getting a 405, please forward a screenshot of this to ${supportEmail}` } if (result.status === 415) { @@ -22766,7 +22905,7 @@ const AngularWorkflow = (defaultprops) => { } if (stringjson.includes("kms/")) { - return "KMS authentication most likely failed. Check your notifications for more details on this page: /admin?admin_tab=notifications. If you need help with KMS, please contact support@shuffler.io" + return `KMS authentication most likely failed. Check your notifications for more details on this page: /admin?admin_tab=notifications. If you need help with KMS, please contact ${supportEmail}` } if (stringjson.includes("invalidurl")) { @@ -22794,7 +22933,7 @@ const AngularWorkflow = (defaultprops) => { if (stringjson.includes("connectionerror")) { if (stringjson.includes("kms")) { - return "KMS authentication most likely failed (2). Check your notifications for more details on this page: /admin?admin_tab=notifications&kms=true. If you need help with KMS, please contact support@shuffler.io" + return `KMS authentication most likely failed (2). Check your notifications for more details on this page: /admin?admin_tab=notifications&kms=true. If you need help with KMS, please contact ${supportEmail}` } return "The URL is incorrect, or Shuffle can't reach it. Set up a Shuffle Environment in the same VLAN, or whitelist Shuffle's IPs." @@ -22815,23 +22954,25 @@ const AngularWorkflow = (defaultprops) => { open={codeModalOpen} PaperProps={{ onClick: () => setActiveDialog("result"), - style: { + sx: { pointerEvents: "auto", - color: "white", - minWidth: isMobile ? "90%" : 750, - padding: 30, - maxHeight: 550, + color: theme.palette.text.primary, + minWidth: isMobile ? "90%" : "750px", + maxHeight: "550px", overflowY: "auto", overflowX: "hidden", border: theme.palette.defaultBorder, - borderRadius: theme.palette.borderRadius, - backgroundColor: "black", + borderRadius: theme.palette.DialogStyle.borderRadius, + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + padding: "30px 30px 30px 30px", + }, }, }} > - {/* Have a sticky top bar */} - + { } }} > - + { } }} > - + { } }} > - + { onClick={(e) => { }} > - + { setCodeModalOpen(false); }} > - + +
{curapp === null ? null : ( @@ -23030,12 +23172,11 @@ const AngularWorkflow = (defaultprops) => { fontSize: 24, marginTop: "auto", marginBottom: "auto", - cursor: "move", }} > {selectedResult.action.label.replaceAll("_", " ")}
-
{selectedResult.action.name}
+
{selectedResult.action.name}
@@ -23154,11 +23295,12 @@ const AngularWorkflow = (defaultprops) => { ) : null}
+ ); const newView = ( -
+
@@ -23399,7 +23541,7 @@ const AngularWorkflow = (defaultprops) => { PaperProps={{ style: { pointerEvents: "auto", - color: "white", + color: theme.palette.text.primary, border: theme.palette.defaultBorder, maxWidth: isMobile ? bodyWidth - 100 : 800, minWidth: isMobile ? bodyWidth - 100 : 800, @@ -23411,7 +23553,7 @@ const AngularWorkflow = (defaultprops) => { > - Runtime Variable + Runtime Variable Runtime Variables are TEMPORARY variables that you can only be set @@ -23420,7 +23562,7 @@ const AngularWorkflow = (defaultprops) => { rel="noopener noreferrer" href="https://shuffler.io/docs/workflows#execution_variables" target="_blank" - style={{ textDecoration: "none", color: "#FF8544" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > here @@ -23431,7 +23573,7 @@ const AngularWorkflow = (defaultprops) => { style={{ marginTop: 25 }} InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} margin="dense" @@ -23446,7 +23588,7 @@ const AngularWorkflow = (defaultprops) => { style={{ marginTop: 25 }} InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} margin="dense" @@ -23514,7 +23656,7 @@ const AngularWorkflow = (defaultprops) => { { PaperProps={{ style: { pointerEvents: "auto", - color: "white", + color: theme.palette.text.primary, border: theme.palette.defaultBorder, maxWidth: isMobile ? bodyWidth - 100 : "100%", @@ -23589,7 +23731,7 @@ const AngularWorkflow = (defaultprops) => { > - Workflow Variable + Workflow Variable { placeholder="Name" InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} margin="dense" @@ -23613,7 +23755,7 @@ const AngularWorkflow = (defaultprops) => { fullWidth InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} defaultValue={newVariableDescription} @@ -23627,7 +23769,7 @@ const AngularWorkflow = (defaultprops) => { margin="dense" InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} fullWidth @@ -23873,7 +24015,7 @@ const AngularWorkflow = (defaultprops) => { return (
-
+
Authentication for {selectedApp.name.replaceAll("_", " ", -1)}
@@ -23882,7 +24024,7 @@ const AngularWorkflow = (defaultprops) => { target="_blank" rel="noopener noreferrer" href="https://shuffler.io/docs/apps#authentication" - style={{ textDecoration: "none", color: "#FF8544" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > What is app authentication? @@ -23965,7 +24107,7 @@ const AngularWorkflow = (defaultprops) => { }} style={{ backgroundColor: theme.palette.surfaceColor, - color: "white", + color: theme.palette.text.primary, height: 50, }} > @@ -23973,7 +24115,7 @@ const AngularWorkflow = (defaultprops) => { key={"false"} style={{ backgroundColor: theme.palette.inputColor, - color: "white", + color: theme.palette.text.primary, }} value={"false"} > @@ -23983,7 +24125,7 @@ const AngularWorkflow = (defaultprops) => { key={"true"} style={{ backgroundColor: theme.palette.inputColor, - color: "white", + color: theme.palette.text.primary, }} value={"true"} > @@ -24047,7 +24189,7 @@ const AngularWorkflow = (defaultprops) => { open={configureWorkflowModalOpen} PaperProps={{ style: { - color: "white", + color: theme.palette.text.primary, minWidth: 650, border: theme.palette.defaultBorder, @@ -24062,7 +24204,7 @@ const AngularWorkflow = (defaultprops) => { position: "absolute", top: 14, right: 14, - color: "white", + color: theme.palette.text.primary, }} onClick={() => { setConfigureWorkflowModalOpen(false); @@ -24104,25 +24246,35 @@ const AngularWorkflow = (defaultprops) => { hideBackdrop={true} disableEnforceFocus={true} disableBackdropClick={true} - style={{ pointerEvents: "none" }} + style={{ + pointerEvents: "none", + }} open={authenticationModalOpen} onClose={() => { setSelectedMeta(undefined) }} PaperProps={{ - style: { + sx: { pointerEvents: "auto", - color: "white", - minWidth: 1100, - minHeight: 700, - maxHeight: 700, - padding: 15, + color: theme.palette.DialogStyle.color, + minWidth: "1100px", + minHeight: "700px", + maxHeight: "700px", overflow: "hidden", zIndex: 10012, - border: theme.palette.defaultBorder, + border: theme.palette.DialogStyle.border, - borderRadius: theme.palette.borderRadius, - backgroundColor: "black", + borderRadius: theme.palette.DialogStyle.borderRadius, + backgroundColor: theme.palette.DialogStyle.backgroundColor, + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogActions-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, }, }} > @@ -24134,6 +24286,7 @@ const AngularWorkflow = (defaultprops) => { right: 75, height: 50, width: 50, + backgroundColor: theme.palette.DialogStyle.backgroundColor, }} > {selectedApp.reference_info === undefined || @@ -24146,7 +24299,7 @@ const AngularWorkflow = (defaultprops) => { rel="noopener noreferrer" target="_blank" href={"https://github.com/shuffle/python-apps"} - style={{ textDecoration: "none", color: "#f86a3e" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > {`Documentation { rel="noopener noreferrer" target="_blank" href={selectedApp.reference_info.github_url} - style={{ textDecoration: "none", color: "#f86a3e" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > {`Documentation { > -
+
{ selectedApp.authentication.parameters === null || selectedApp.authentication.parameters === undefined || selectedApp.authentication.parameters.length === 0 ? 0 : 2, - padding: 0, + paddingLeft: 15, + paddingTop: 15, + paddingBottom: 15, minHeight: isMobile ? "90%" : 700, maxHeight: isMobile ? "90%" : 700, overflowY: "auto", overflowX: isMobile ? "auto" : "hidden", + backgroundColor: theme.palette.DialogStyle.backgroundColor, }} > {authenticationType.type === "oauth2" || authenticationType.type === "oauth2-app" ? @@ -24266,6 +24422,7 @@ const AngularWorkflow = (defaultprops) => { minHeight: 630, overflowY: "auto", overflowX: "hidden", + backgroundColor: theme.palette.DialogStyle.backgroundColor, }} onLoad={() => { /* @@ -24353,7 +24510,7 @@ const AngularWorkflow = (defaultprops) => { rel="noopener noreferrer" target="_blank" href="https://discord.gg/B2CBzUm" - style={{ textDecoration: "none", color: "#f86a3e" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > Join the community on Discord! @@ -24373,7 +24530,7 @@ const AngularWorkflow = (defaultprops) => { rel="noopener noreferrer" target="_blank" href={"https://github.com/shuffle/python-apps"} - style={{ textDecoration: "none", color: "#f86a3e" }} + style={{ textDecoration: "none", color: theme.palette.linkColor }} > Check it out on Github! @@ -24415,7 +24572,7 @@ const AngularWorkflow = (defaultprops) => { href={selectedMeta.link} style={{ textDecoration: "none", color: "#FF8544" }} > - @@ -24426,13 +24583,13 @@ const AngularWorkflow = (defaultprops) => { style={{ height: "100%", width: 1, - backgroundColor: "white", + backgroundColor: theme.palette.text.primary, marginLeft: 50, marginRight: 50, }} /> )} - + {selectedMeta.read_time} minute {selectedMeta.read_time === 1 ? "" : "s"} to read @@ -24512,7 +24669,7 @@ const AngularWorkflow = (defaultprops) => { PaperProps={{ style: { pointerEvents: "auto", - color: "white", + color: theme.palette.text.primary, minWidth: 600, minHeight: 250, maxHeight: 250, @@ -24527,7 +24684,7 @@ const AngularWorkflow = (defaultprops) => { }} > -
Run a Tenzir Pipeline
+
Run a Tenzir Pipeline
Runs a Tenzir pipeline. You can use the output of the pipeline in your workflow. @@ -24579,7 +24736,7 @@ const AngularWorkflow = (defaultprops) => { return (
{/* - + */} { }); }} > - +
{ @@ -24950,7 +25107,7 @@ const AngularWorkflow = (defaultprops) => { minWidth: isMobile ? "100%" : 360, maxWidth: isMobile ? "100%" : 360, backgroundColor: theme.palette.platformColor, - color: "white", + color: theme.palette.text.primary, fontSize: 18, zIndex: 15001, borderRight: theme.palette.defaultBorder, @@ -24980,7 +25137,7 @@ const AngularWorkflow = (defaultprops) => { : null*/}
-
+
{selectedVersion?.name} @@ -25350,7 +25507,7 @@ const AngularWorkflow = (defaultprops) => { position: "absolute", top: 6, right: 6, - color: "white", + color: theme.palette.text.primary, }} onClick={() => { setSelectionOpen(false) @@ -25384,7 +25541,7 @@ const AngularWorkflow = (defaultprops) => { position: "absolute", top: 6, right: 6, - color: "white", + color: theme.palette.text.primary, }} onClick={() => { setShowVideo("") diff --git a/frontend/src/views/ApiExplorerWrapper.jsx b/frontend/src/views/ApiExplorerWrapper.jsx index 96298437..45f9d762 100644 --- a/frontend/src/views/ApiExplorerWrapper.jsx +++ b/frontend/src/views/ApiExplorerWrapper.jsx @@ -59,6 +59,7 @@ const ApiExplorer = React.lazy(() => import("../components/ApiExplorer.jsx")); const ApiExplorerWrapper = (props) => { const { globalUrl, serverside, userdata, isLoggedIn, isLoaded} = props; + const { supportEmail } = useContext(Context); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" const location = useLocation(); const navigate = useNavigate(); @@ -165,7 +166,7 @@ const ApiExplorerWrapper = (props) => { } if (!found) { - toast.error(`Failed to get API data for '${appname}' (1). Contact support@shuffler.io if this persists.`, { + toast.error(`Failed to get API data for '${appname}' (1). Contact ${supportEmail} if this persists.`, { "autoClose": 10000, }) @@ -174,7 +175,7 @@ const ApiExplorerWrapper = (props) => { },3000) } } else { - toast.error(`Failed to get API data for '${appname}' (2). Contact support@shuffler.io if this persists.`, { + toast.error(`Failed to get API data for '${appname}' (2). Contact ${supportEmail} if this persists.`, { "autoClose": 10000, }) setTimeout(()=>{ @@ -539,7 +540,7 @@ const ApiExplorerWrapper = (props) => { }else if (openapi?.id?.length > 0) { appid = openapi?.id; }else{ - toast.error("App id is missing and we can't run the API. Please contact support@shuffler.io if this persists."); + toast.error(`App id is missing and we can't run the API. Please contact ${supportEmail} if this persists.`); return; } @@ -673,7 +674,7 @@ const ApiExplorerWrapper = (props) => { if (data.result.includes("custom_action doesn't exist")) { // No timeout error - toast.info("This API is being rebuilt due to missing functionality. Please wait a minute or two, then try again. If this persists, please report to support@shuffler.io", { + toast.info(`This API is being rebuilt due to missing functionality. Please wait a minute or two, then try again. If this persists, please report to ${supportEmail}`, { "autoClose": 90000, }) } else if (data.result.includes("authentication") && data.result.includes("Oauth2")) { diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index ae234e9e..ad7aaf48 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1,7 +1,7 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { makeStyles } from "@mui/styles"; import { BrowserView, MobileView } from "react-device-detect"; -import theme from '../theme.jsx'; +import { getTheme } from '../theme.jsx'; import { Paper, @@ -57,6 +57,7 @@ import { ToastContainer, toast } from "react-toastify" import words from "shellwords"; import AvatarEditor from "react-avatar-editor"; +import { Context } from "../context/ContextApi.jsx"; const surfaceColor = "#27292D"; const inputColor = "#383B40"; @@ -67,33 +68,7 @@ const bodyDivStyle = { zoom: 0.8, }; -const actionListStyle = { - paddingLeft: "10px", - paddingRight: "10px", - paddingBottom: "10px", - paddingTop: "10px", - marginTop: "5px", - display: "flex", - color: "white", - position: "relative", - backgroundColor: theme.palette.platformColor, -}; - -const boxStyle = { - color: "white", - flex: "1", - marginLeft: "10px", - marginRight: "10px", - paddingLeft: "30px", - paddingRight: "30px", - paddingBottom: "30px", - paddingTop: "30px", - display: "flex", - flexDirection: "column", - - backgroundColor: theme.palette.backgroundColor, -}; const dividerStyle = { marginBottom: "10px", @@ -427,9 +402,40 @@ const AppCreator = (defaultprops) => { const [actionAmount, setActionAmount] = useState(increaseAmount); const [newAppGroup, setNewAppGroup] = useState("") + const { themeMode, supportEmail, brandColor } = useContext(Context); + const theme = getTheme(themeMode, brandColor); + const [oauth2Scopes, setOauth2Scopes] = useState([]); const [oauth2Type, setOauth2Type] = useState("delegated"); + const actionListStyle = { + paddingLeft: "10px", + paddingRight: "10px", + paddingBottom: "10px", + paddingTop: "10px", + marginTop: "5px", + display: "flex", + color: theme.palette.text.primary, + position: "relative", + + backgroundColor: theme.palette.platformColor, + }; + + const boxStyle = { + color: theme.palette.text.primary, + flex: "1", + marginLeft: "10px", + marginRight: "10px", + paddingLeft: "30px", + paddingRight: "30px", + paddingBottom: "30px", + paddingTop: "30px", + display: "flex", + flexDirection: "column", + + backgroundColor: theme.palette.platformColor, + }; + //client_credentials const [oauth2GrantType, setOauth2GrantType] = useState(""); const defaultAuth = { @@ -2626,7 +2632,7 @@ const AppCreator = (defaultprops) => { if (response.status === 403) { var urlParams = new URLSearchParams(window.location.search) if (urlParams.has("id")) { - toast.error("Please log in to build this app. If this error persists, please contact support@shuffler.io") + toast.error(`Please log in to build this app. If this error persists, please contact ${supportEmail}`) } else { toast.error("Failed to save the app as you are not the owner. Redirecting you to the forking page. When there, save again.") if (props.match.params.appid !== undefined && props.match.params.appid !== null && props.match.params.appid.length > 0) { @@ -2678,12 +2684,12 @@ const AppCreator = (defaultprops) => { const bearerAuth = authenticationOption === "Bearer auth" ? ( -
+

Bearer auth @@ -2696,12 +2702,12 @@ const AppCreator = (defaultprops) => { // Basicauth const basicAuth = authenticationOption === "Basic auth" ? ( -
+

Basic authentication @@ -2795,7 +2801,7 @@ const AppCreator = (defaultprops) => { flex: 2, marginTop: 0, marginBottom: 0, - backgroundColor: inputColor, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, marginRight: 5, }} fullWidth={true} @@ -2813,7 +2819,8 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.textFieldStyle.color, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, minHeight: 50, marginLeft: 5, maxWidth: "95%", @@ -2828,7 +2835,7 @@ const AppCreator = (defaultprops) => { marginTop: 0, marginBottom: 0, flex: 2, - backgroundColor: inputColor, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, marginRight: 5, }} fullWidth={true} @@ -2843,7 +2850,8 @@ const AppCreator = (defaultprops) => { }} InputProps={{ style: { - color: "white", + color: theme.palette.textFieldStyle.color, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, minHeight: 50, marginLeft: 5, maxWidth: "95%", @@ -2861,9 +2869,9 @@ const AppCreator = (defaultprops) => { value={extraAuth[index].type} style={{ flex: 1, - backgroundColor: inputColor, + backgroundColor: theme.palette.backgroundColor, paddingLeft: "10px", - color: "white", + color: theme.palette.text.primary, height: 50, borderRadius: theme.shape.borderRadius, }} @@ -2874,14 +2882,14 @@ const AppCreator = (defaultprops) => { > Header Query @@ -2929,7 +2937,7 @@ const AppCreator = (defaultprops) => { const jwtAuth = authenticationOption === "JWT" ? ( -
+
JWT authentication { variant="outlined" defaultValue={parameterName} helperText={ - + Must start with / and be a valid path } @@ -2958,7 +2966,7 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -2979,7 +2987,7 @@ const AppCreator = (defaultprops) => { variant="outlined" defaultValue={parameterName} helperText={ - + Must use 'key=value&key=value' format } @@ -2991,7 +2999,7 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -3001,7 +3009,7 @@ const AppCreator = (defaultprops) => { const oauth2Auth = authenticationOption === "Oauth2" ? ( -
+
Oauth2 authentication { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -3111,7 +3119,7 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -3160,7 +3168,7 @@ const AppCreator = (defaultprops) => { }} InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -3177,7 +3185,7 @@ const AppCreator = (defaultprops) => { required InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, maxHeight: 160, }, }} @@ -3202,7 +3210,7 @@ const AppCreator = (defaultprops) => { const apiKey = authenticationOption === "API key" ? ( -
+
API key authentication Do NOT put your actual API-key. Add the name of the field used for authentication, e.g. "X-APIKEY". @@ -3221,7 +3229,7 @@ const AppCreator = (defaultprops) => { variant="outlined" value={parameterName} helperText={ - + Can't be empty or contain any of the following: !#$%&'^"+-._~|]+$:= } @@ -3238,7 +3246,7 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -3255,7 +3263,7 @@ const AppCreator = (defaultprops) => { borderRadius: theme.shape.borderRadius, backgroundColor: inputColor, paddingLeft: 10, - color: "white", + color: theme.palette.text.primary, height: 57, }} inputProps={{ @@ -3271,7 +3279,7 @@ const AppCreator = (defaultprops) => { return ( {data} @@ -3463,15 +3471,14 @@ const AppCreator = (defaultprops) => { { @@ -3557,7 +3564,7 @@ const AppCreator = (defaultprops) => { placeholder={"Query name (key)"} label={"Query Key"} helperText={ - + Click required to flip } @@ -3569,7 +3576,7 @@ const AppCreator = (defaultprops) => { style={{flex: 3}} InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -3589,7 +3596,7 @@ const AppCreator = (defaultprops) => { style={{flex: 2}} InputProps={{ style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -3662,7 +3669,7 @@ const AppCreator = (defaultprops) => { )} { }} key={currentAction} helperText={ - + Shows an example body to the user. ${} creates variables. } @@ -3687,7 +3694,8 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.textFieldStyle.color, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, }, }} /> @@ -3700,7 +3708,7 @@ const AppCreator = (defaultprops) => { Example success response { defaultValue={currentAction["example_response"]} onChange={(e) => setActionField("example_response", e.target.value)} helperText={ - + Helps with autocompletion and understanding of the endpoint } key={currentAction} InputProps={{ style: { - color: "white", + color: theme.palette.textFieldStyle.color, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, }, }} /> @@ -3794,8 +3803,8 @@ const AppCreator = (defaultprops) => { fullWidth PaperProps={{ style: { - backgroundColor: surfaceColor, - color: "white", + backgroundColor: theme.palette.drawer.backgroundColor, + color: theme.palette.text.primary, minWidth: 700, maxWidth: 700, }, @@ -3812,15 +3821,15 @@ const AppCreator = (defaultprops) => { setFileUploadEnabled(false); }} > - + -
New action
+
New action
Learn more about actions @@ -3832,7 +3841,8 @@ const AppCreator = (defaultprops) => { flex: "1", marginTop: 5, marginRight: 15, - backgroundColor: inputColor, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }} fullWidth={true} placeholder="Name" @@ -3871,7 +3881,8 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }, }} /> @@ -3883,7 +3894,8 @@ const AppCreator = (defaultprops) => { flex: "1", marginTop: 5, marginRight: "15px", - backgroundColor: inputColor, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }} fullWidth={true} placeholder="Description" @@ -3895,7 +3907,8 @@ const AppCreator = (defaultprops) => { onChange={(e) => setActionField("description", e.target.value)} InputProps={{ style: { - color: "white", + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }, }} /> @@ -3917,14 +3930,18 @@ const AppCreator = (defaultprops) => { }} value={currentActionMethod} style={{ - backgroundColor: inputColor, paddingLeft: "10px", - color: "white", + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, height: "50px", }} inputProps={{ name: "Method", id: "method-option", + style: { + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, + }, }} > @@ -3939,7 +3956,7 @@ const AppCreator = (defaultprops) => { > { flex: "1", marginRight: "15px", marginTop: "5px", - backgroundColor: inputColor, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }} fullWidth={true} placeholder="URL path" @@ -3976,7 +3994,7 @@ const AppCreator = (defaultprops) => { setUrlPath(e.target.value); }} helperText={ - + The path to use. Must start with /. Use {"{variablename}"} to have path variables @@ -3987,7 +4005,8 @@ const AppCreator = (defaultprops) => { input: classes.input, }, style: { - color: "white", + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }, }} onBlur={(event) => { @@ -4270,7 +4289,7 @@ const AppCreator = (defaultprops) => { defaultValue={currentAction["file_field"]} onChange={(e) => setActionField("file_field", e.target.value)} helperText={ - + The File field to interact with } @@ -4279,7 +4298,7 @@ const AppCreator = (defaultprops) => { notchedOutline: classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.text.primary, }, }} /> @@ -4294,7 +4313,8 @@ const AppCreator = (defaultprops) => { flex: "1", marginRight: "15px", marginTop: "5px", - backgroundColor: inputColor, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }} fullWidth={true} placeholder={ @@ -4308,13 +4328,14 @@ const AppCreator = (defaultprops) => { minRows="2" onChange={(e) => setActionField("headers", e.target.value)} helperText={ - + Headers that are part of the request. Default: EMPTY } InputProps={{ style: { - color: "white", + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, }, }} /> @@ -4323,14 +4344,14 @@ const AppCreator = (defaultprops) => { {bodyInfo} {exampleResponse} -
+
@@ -3919,13 +3923,13 @@ const AppExplorer = (props) => { -

+

Apps

@@ -3971,9 +3975,7 @@ const AppExplorer = (props) => { ) : null} {appType === 0 || appType === 2 ? ( { const data = openapi; let exportFileDefaultName = name + ".json"; @@ -4020,7 +4022,7 @@ const AppExplorer = (props) => { }} > - + ) : null} @@ -4036,7 +4038,7 @@ const AppExplorer = (props) => { }} > - + : null} @@ -4197,8 +4199,8 @@ const AppExplorer = (props) => { }} style={{ width: 150, - backgroundColor: theme.palette.surfaceColor, - color: "white", + backgroundColor: theme.palette.backgroundColor, + color: theme.palette.text.primary, height: 40, marginTop: 5 }} @@ -4213,7 +4215,7 @@ const AppExplorer = (props) => { return ( {data} @@ -4373,8 +4375,8 @@ const AppExplorer = (props) => { style={{ borderRadius: 25, fontSize: 11, - color: "white", - backgroundColor: "rgba(255,255,255,0)", + color: theme.palette.text.primary, + backgroundColor: theme.palette.platformColor, border: "1px solid #ddf4e1", textTransform: "none", marginLeft: 4, @@ -4466,6 +4468,11 @@ const AppExplorer = (props) => { color: "white", textAlign: "center", }} + sx={{ + "&:hover": { + backgroundColor: theme.palette.hoverColor, + }, + }} > { color: "white", textAlign: "center", }} + sx={{ + "&:hover": { + backgroundColor: theme.palette.hoverColor, + }, + }} > { handleAppClick(data); @@ -150,7 +152,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", - color: '#F1F1F1' + color: theme.palette.text.primary, }}> {data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
@@ -161,7 +163,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, textOverflow: "ellipsis", whiteSpace: "nowrap", marginLeft: 8, - color: "rgba(158, 158, 158, 1)" + color: theme.palette.text.secondary, }}> {data.categories ? data.categories.join(", ") : "NA"}
@@ -172,7 +174,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, whiteSpace: "nowrap", width: "100%", marginLeft: 8, - color: "rgba(158, 158, 158, 1)", + color: theme.palette.text.secondary, display: "flex", justifyContent: 'space-between', paddingRight: 15, @@ -202,7 +204,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, }}> { canEditApp ? ( - + ) : ( - + ) }
@@ -569,7 +572,7 @@ const Hits = ({ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", - color: "rgba(158, 158, 158, 1)", + color: theme.palette.text.secondary, marginTop: 5, }} > @@ -600,12 +603,12 @@ const Hits = ({ componentsProps={{ tooltip: { sx: { - backgroundColor: "rgba(33, 33, 33, 1)", - color: "rgba(241, 241, 241, 1)", + backgroundColor: theme.palette.tooltip.backgroundColor, + color: theme.palette.tooltip.color, width: "auto", height: "auto", fontSize: 16, - border: "1px solid rgba(73, 73, 73, 1)", + border: theme.palette.tooltip.border, } } }} @@ -650,15 +653,15 @@ const Hits = ({
{allActivatedAppIds && allActivatedAppIds?.includes(data.objectID) ? ( +
+ : null} + + + {integrationPartner ? ( +
+ Support Email + + { + setSupportEmail(e.target.value) + }} + color="primary" + InputProps={{ + style: { + color: theme.palette.textFieldStyle.color, + height: "35px", + fontSize: "1em", + borderRadius: 4, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + }, + }} + /> + + +
+ ) : null} + + {integrationPartner ? ( +
+ Logout URL + + { + setLogoutUrl(e.target.value) + }} + color="primary" + InputProps={{ + style: { + color: theme.palette.textFieldStyle.color, + height: "35px", + fontSize: "1em", + borderRadius: 4, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + }, + }} + /> + + +
+ ) : null}
) diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index e28b73f5..9cff6231 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -680,7 +680,7 @@ const CacheView = memo((props) => { labelId="input-namespace-select-label" id="input-namespace-select-id" style={{ - color: "white", + color: theme.palette.textFieldStyle.color, minWidth: 122, maxWidth: 122, height: 35, @@ -719,7 +719,7 @@ const CacheView = memo((props) => { {data.replaceAll("_", " ")} @@ -826,7 +826,7 @@ const CacheView = memo((props) => { }} InputProps={{ style: { - color: "white", + color: theme.palette.textFieldStyle.color, height: 35, fontSize: 16, borderRadius: 4, diff --git a/frontend/src/components/CloudSyncTab.jsx b/frontend/src/components/CloudSyncTab.jsx index 24a043fa..d4aaa072 100644 --- a/frontend/src/components/CloudSyncTab.jsx +++ b/frontend/src/components/CloudSyncTab.jsx @@ -644,14 +644,15 @@ const CloudSyncTab = (props) => { { } }, [usersWorkFlows]); + const handleChangeTheme = (newTheme) => { + + toast.info("Changing theme to " + newTheme + " - please wait!"); + + const data = { + "org_id": userdata?.active_org?.id, + }; + + data["branding"] = { + "theme": newTheme, + "enable_chat": userdata?.active_org?.branding?.enable_chat || false, + "home_url": userdata.active_org?.branding?.home_url || "", + "brand_color": userdata?.active_org?.branding?.brand_color || theme.palette.primary.main, + "brand_name": userdata?.active_org?.branding?.brand_name || "", + "logout_url": userdata?.active_org?.branding?.logout_url || "", + "support_email": userdata?.active_org?.branding?.support_email || "", + } + + data["editing_branding"] = true; + + const url = globalUrl + `/api/v1/orgs/${userdata?.active_org?.id}`; + fetch(url, { + mode: "cors", + method: "POST", + body: JSON.stringify(data), + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === false) { + toast("Failed updating org: ", responseJson.reason); + } else { + handleThemeChange(newTheme) + setCurrentSelectedTheme(newTheme); + } + + }) + ) + .catch((error) => { + console.log("Error changing theme: ", error); + }); + }; + + + const removeCookie = (name, path = "/") => { document.cookie = `${name}=; path=${path}; expires=Thu, 01 Jan 1970 00:00:00 GMT;`; }; @@ -505,9 +555,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { if (newTheme === currentSelectedTheme) { return; } - console.log("Selected theme:", newTheme); - setCurrentSelectedTheme(newTheme) - handleThemeChange(newTheme) + handleChangeTheme(newTheme); }} aria-label="theme" style={{display: 'flex', justifyContent: "center", marginBottom: 10, }} @@ -587,7 +635,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { - Version: 2.1.0-beta + Version: 2.0.2 @@ -2022,4 +2070,4 @@ const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, s ) ) -}); +}); \ No newline at end of file diff --git a/frontend/src/components/OrgHeaderexpandedNew.jsx b/frontend/src/components/OrgHeaderexpandedNew.jsx index 4f708800..525491e6 100644 --- a/frontend/src/components/OrgHeaderexpandedNew.jsx +++ b/frontend/src/components/OrgHeaderexpandedNew.jsx @@ -448,7 +448,7 @@ const OrgHeaderexpandedNew = (props) => {
-
+
@@ -931,7 +931,7 @@ const OrgHeaderexpandedNew = (props) => { marginTop: "8px", marginRight: "16px", height: 35, - backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, + backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor, }} fullWidth={true} type="name" @@ -948,7 +948,8 @@ const OrgHeaderexpandedNew = (props) => { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.textFieldStyle.color, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, fontWeight: 400, fontSize: 16, @@ -970,7 +971,7 @@ const OrgHeaderexpandedNew = (props) => { flex: "1", marginTop: "8px", marginRight: "15px", - backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, + backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor, height: 35, }} fullWidth={true} @@ -988,7 +989,7 @@ const OrgHeaderexpandedNew = (props) => { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.textFieldStyle.color, fontWeight: 400, fontSize: 16, @@ -1010,7 +1011,7 @@ const OrgHeaderexpandedNew = (props) => { flex: "1", marginTop: "5px", marginRight: "15px", - backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, + backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor, height: 35, }} fullWidth={true} @@ -1028,7 +1029,7 @@ const OrgHeaderexpandedNew = (props) => { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.textFieldStyle.color, fontWeight: 400, fontSize: 16, @@ -1050,7 +1051,7 @@ const OrgHeaderexpandedNew = (props) => { flex: "1", marginTop: "5px", marginRight: "15px", - backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor, + backgroundColor: isEditOrgTab ? theme.palette.textFieldStyle.backgroundColor : theme.palette.inputColor, height: 35, }} fullWidth={true} @@ -1068,7 +1069,7 @@ const OrgHeaderexpandedNew = (props) => { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { - color: "white", + color: theme.palette.textFieldStyle.color, fontWeight: 400, fontSize: 16, diff --git a/frontend/src/components/UserManagmentTab.jsx b/frontend/src/components/UserManagmentTab.jsx index ec7036cc..2346a5a4 100644 --- a/frontend/src/components/UserManagmentTab.jsx +++ b/frontend/src/components/UserManagmentTab.jsx @@ -657,7 +657,7 @@ const UserManagmentTab = memo((props) => { InputProps={{ style: { height: "50px", - color: "white", + color: theme.palette.textFieldStyle.color, fontSize: "1em", }, }} @@ -772,7 +772,7 @@ const UserManagmentTab = memo((props) => { InputProps={{ style: { height: 50, - color: "white", + color: theme.palette.textFieldStyle.color, }, }} color="primary" @@ -815,7 +815,7 @@ const UserManagmentTab = memo((props) => { InputProps={{ style: { height: 50, - color: "white", + color: theme.palette.textFieldStyle.color, }, }} color="primary" @@ -956,7 +956,7 @@ const UserManagmentTab = memo((props) => { InputProps={{ style: { height: 50, - color: "white", + color: theme.palette.textFieldStyle.color, }, }} color="primary" @@ -1027,7 +1027,7 @@ const UserManagmentTab = memo((props) => { InputProps={{ style: { height: 50, - color: "white", + color: theme.palette.textFieldStyle.color, fontSize: "1em", }, maxLength: 6, @@ -1135,7 +1135,7 @@ const UserManagmentTab = memo((props) => { }} > - User Logs + User Logs {/* ask user for which IP they want to see logs for by iterating of user.login_info */} From 400a3653acbda5cda6bae69356dee68aa517cf47 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 19 May 2025 21:42:50 +0200 Subject: [PATCH 165/200] Some CDN changes wut --- frontend/src/components/UserManagmentTab.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/UserManagmentTab.jsx b/frontend/src/components/UserManagmentTab.jsx index ec7036cc..8efd3e30 100644 --- a/frontend/src/components/UserManagmentTab.jsx +++ b/frontend/src/components/UserManagmentTab.jsx @@ -1359,7 +1359,7 @@ const UserManagmentTab = memo((props) => { }} > - {["Username", /*"API Key",*/ "Role", /*"Active",*/ "Type", "MFA", ...(selectedOrganization?.child_orgs?.length > 0 ? ["Suborgs"]: []), "Actions", "Last Login"].map((header, index) => ( + {["Region", "Username", /*"API Key",*/ "Role", /*"Active",*/ "Type", "MFA", ...(selectedOrganization?.child_orgs?.length > 0 ? ["Suborgs"]: []), "Actions", "Last Login"].map((header, index) => ( { return ( + )} + style={{ display: 'table-cell', verticalAlign: 'middle', textAlign: 'center' }} + /> From 8814463a27e3def83159c4b9938191c0ceee9d04 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 19 May 2025 21:43:35 +0200 Subject: [PATCH 166/200] Sync over from shaffuru --- frontend/src/components/LicencePopup.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 6 ++++-- frontend/src/views/Apps2.jsx | 12 ++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index eb7d719f..0e580cbb 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -1242,7 +1242,7 @@ const LicencePopup = (props) => {
- {selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0 ? + {(selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0) && isCloud ? { Name { Delay { style={{ height: 45, minWidth: 45, - backgroundColor: "#2F2F2F", + backgroundColor: theme.palette.platformColor, borderRadius: 4, padding: "8px 16px", }} @@ -1950,9 +1950,9 @@ const Apps2 = (props) => { }} > {isLoading ? ( - + ) : ( - + )} @@ -1980,7 +1980,7 @@ const Apps2 = (props) => { style={{ height: 45, minWidth: 45, - backgroundColor: "#2F2F2F", + backgroundColor: theme.palette.platformColor, borderRadius: 4, padding: "8px 16px", }} @@ -1992,9 +1992,9 @@ const Apps2 = (props) => { }} > {isLoading ? ( - + ) : ( - + )} From f3678fcfdc1f82b2de57bfa57746a92f39c516f7 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Tue, 20 May 2025 01:23:25 +0530 Subject: [PATCH 167/200] fix for defaultNetwork failing service deployments --- functions/onprem/orborus/orborus.go | 58 ++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 3697735e..4b3076b6 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -610,13 +610,31 @@ func deployServiceWorkers(image string) { if defaultNetworkAttach == true || strings.ToLower(os.Getenv("SHUFFLE_DEFAULT_NETWORK_ATTACH")) == "true" { targetName := "shuffle_shuffle" - log.Printf("[DEBUG] Adding network attach for network %s to worker in swarm", targetName) - serviceSpec.Networks = append(serviceSpec.Networks, swarm.NetworkAttachmentConfig{ - Target: targetName, - }) + isAttachable := false + networks, err := dockercli.NetworkList(ctx, network.ListOptions{}) + if err == nil { + for _, net := range networks { + if net.Name == targetName { + if net.Scope == "swarm" { + log.Printf("[DEBUG] Found swarm-scoped network: %s", targetName) + isAttachable = true + } else { + log.Printf("[WARNING] Network %s exist but is not swarm scoped (scope=%s)", targetName, net.Scope) + } + break + } + } + } - // FIXM: Remove this if deployment fails? - serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_SWARM_OTHER_NETWORK=%s", targetName)) + if isAttachable { + log.Printf("[DEBUG] Adding network attach for network %s to worker in swarm", targetName) + serviceSpec.Networks = append(serviceSpec.Networks, swarm.NetworkAttachmentConfig{ + Target: targetName, + }) + + // FIXM: Remove this if deployment fails? + serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_SWARM_OTHER_NETWORK=%s", targetName)) + } } if dockerApiVersion != "" { @@ -709,6 +727,34 @@ func deployServiceWorkers(image string) { } else { if !strings.Contains(fmt.Sprintf("%s", err), "Already Exists") && !strings.Contains(fmt.Sprintf("%s", err), "is already in use by service") { log.Printf("[ERROR] Failed making service: %s", err) + if strings.Contains(fmt.Sprintf("%s", err), "networks scoped to the swarm can be used") { + log.Printf("[WARNING] Swarm network attachment failed, retrying without shuffle_shuffle") + + var updatedNetworks []swarm.NetworkAttachmentConfig + for _, net := range serviceSpec.Networks { + if net.Target != "shuffle_shuffle" { + updatedNetworks = append(updatedNetworks, net) + } + } + serviceSpec.Networks = updatedNetworks + + var updatedEnv []string + for _, env := range serviceSpec.TaskTemplate.ContainerSpec.Env { + if !strings.HasPrefix(env, "SHUFFLE_SWARM_OTHER_NETWORK=") { + updatedEnv = append(updatedEnv, env) + } + } + serviceSpec.TaskTemplate.ContainerSpec.Env = updatedEnv + serviceOptions := types.ServiceCreateOptions{} + _, err = dockercli.ServiceCreate( + ctx, + serviceSpec, + serviceOptions, + ) + if err != nil { + log.Printf("[ERROR] Failed to deploy service even without shuffle_shuffle network: %s", err) + } + } } else { log.Printf("[WARNING] Failed deploying workers: %s", err) if len(serviceSpec.Networks) > 1 { From c2c4a9c3235108f707c0f78fc04bf58a3c6387c1 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 20 May 2025 01:07:16 +0200 Subject: [PATCH 168/200] Minor fixes for testing Singul locally --- backend/go-app/go.mod | 4 ++-- backend/go-app/walkoff.go | 19 ++++++++++------- frontend/src/views/AngularWorkflow.jsx | 29 ++++++++++++++++++-------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index c630b9ff..9accdf46 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.8 //replace github.com/frikky/schemaless => ../../../schemaless //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi -//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared require ( cloud.google.com/go/datastore v1.20.0 @@ -22,7 +22,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.58 + github.com/shuffle/shuffle-shared v0.8.60 golang.org/x/crypto v0.37.0 google.golang.org/api v0.228.0 google.golang.org/grpc v1.71.1 diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 8357de3e..4329e4af 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1905,8 +1905,8 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { err = shuffle.SetSchedule(ctx, newSchedule) if err != nil { - log.Printf("Failed setting cloud schedule: %s", err) - resp.WriteHeader(401) + log.Printf("[ERROR] Failed setting cloud schedule: %s", err) + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) return } @@ -1941,17 +1941,22 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { // FIXME - real error message lol if err != nil { - log.Printf("Failed creating schedule: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Invalid argument. Try cron */15 * * * *"}`))) + log.Printf("[ERROR] Failed creating schedule: %s", err) + + resp.WriteHeader(400) + if schedule.Environment == "cloud" { + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Invalid argument. For cloud schedules, try cron */15 * * * *"}`))) + } else { + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Invalid argument. For onprem schedules, try 60 for 60 seconds"}`))) + } return } //workflow.Schedules = append(workflow.Schedules, schedule) err = shuffle.SetWorkflow(ctx, *workflow, workflow.ID) if err != nil { - log.Printf("Failed setting workflow for schedule: %s", err) - resp.WriteHeader(401) + log.Printf("[ERROR] Failed setting workflow for schedule: %s", err) + resp.WriteHeader(400) resp.Write([]byte(`{"success": false}`)) return } diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 37e2e056..db5111bd 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -790,7 +790,7 @@ const AngularWorkflow = (defaultprops) => { }, { "name": "fields", - "value": "", + "value": '{\n "ticket_id": "123456",\n "comment": "This is a comment"\n}', "required": false, "multiline": true, }, @@ -7000,7 +7000,11 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (responseJson.success === false) { - toast("Failed to auto-activate the app. Go to /apps and activate it.") + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + toast.error("Failed to auto-activate the app: " + responseJson.reason) + } else { + toast.error("Failed to auto-activate the app. Go to /apps and activate it.") + } } else { if (refresh === true) { setHighlightedApp(appid) @@ -10312,7 +10316,7 @@ const AngularWorkflow = (defaultprops) => { } } - toast("Creating schedule") + toast.info("Creating schedule") var data = { name: trigger.name, frequency: workflow.triggers[triggerindex].parameters[0].value, @@ -10361,9 +10365,9 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { - toast("Failed to set schedule: " + responseJson.reason); + toast.error("Failed to set schedule: " + responseJson.reason); } else { - toast("Successfully created schedule"); + toast.success("Successfully created schedule"); workflow.triggers[triggerindex].status = "running"; trigger.status = "running"; setSelectedTrigger(trigger); @@ -11876,8 +11880,9 @@ const AngularWorkflow = (defaultprops) => { var type = "app" const baseImage = + const width = 230 return ( -
+
{hits.length === 0 ? @@ -11971,7 +11976,7 @@ const AngularWorkflow = (defaultprops) => { }} defaultPosition={{ x: 0, y: 0 }} > -
{ +
{ clickedApp(hit) }}> @@ -12125,7 +12130,12 @@ const AngularWorkflow = (defaultprops) => { } if ((app.id === "integration" || app.id === "shuffle_agent") && userdata.support !== true) { - return null + console.log("APPID: ", app.id, isCloud) + if (isCloud === false && app.id === "integration") { + } else { + console.log("RETURNING", app.id) + return null + } } if (viewedApps.includes(app.id)) { @@ -12190,7 +12200,7 @@ const AngularWorkflow = (defaultprops) => {
) : apps.length > 0 ? (
{ console.log("Should load in extra apps?") }} @@ -12198,6 +12208,7 @@ const AngularWorkflow = (defaultprops) => { Couldn't find the apps you were looking for? Searching unactivated apps. Click one of these apps to Activate it for your organisation. + { console.log("CLICKED") }}> From 4942387bab89c4aca1cb00a6cec25d9b9235be76 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 20 May 2025 13:51:36 +0530 Subject: [PATCH 169/200] add theming changes for onprem --- backend/go-app/main.go | 2 ++ frontend/src/App.jsx | 5 ++++ frontend/src/components/LeftSideBar.jsx | 38 +++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 60f162b4..a03f7d28 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1093,6 +1093,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { Priorities: orgPriorities, Licensed: licensed, ActiveApps: activatedAppIds, + Theme: userInfo.Theme, } returnData, err := json.Marshal(returnValue) @@ -5104,6 +5105,7 @@ func initHandlers() { r.HandleFunc("/api/v1/users/{key}/get2fa", shuffle.HandleGet2fa).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/{key}/set2fa", shuffle.HandleSet2fa).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users", shuffle.HandleGetUsers).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/users/{userid}/theme", shuffle.HandleSetUserTheme).Methods("POST", "OPTIONS") // General - duplicates and old. r.HandleFunc("/api/v1/getusers", shuffle.HandleGetUsers).Methods("GET", "OPTIONS") diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index fa3ac7ea..7a8c31da 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -192,6 +192,11 @@ const App = (message, props) => { { path: "/" } ); } + if (responseJson?.theme?.length > 0) { + handleThemeChange(responseJson.theme) + }else{ + handleThemeChange("dark") + } } // Handling Ethereum update diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 2d9bad44..db93754b 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -449,6 +449,36 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }); }; + const handleUpdateTheme = (newTheme) => { + + const data = { + "theme": newTheme, + } + + const url = globalUrl + `/api/v1/users/${userdata?.id}/theme`; + fetch(url, { + mode: "cors", + method: "POST", + body: JSON.stringify(data), + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }).then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === false) { + toast("Failed updating theme: ", responseJson.reason); + } else { + handleThemeChange(newTheme); + setCurrentSelectedTheme(newTheme); + } + }) + ).catch((error) => { + console.log("Error changing theme: ", error); + }); + }; const removeCookie = (name, path = "/") => { @@ -543,7 +573,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }, }} > - {userdata && ( + {userdata && (userdata?.org_status?.includes("integration_partner") && userdata?.org_status?.includes("sub_org")) ? null : ( <> { if (newTheme === currentSelectedTheme) { return; } - handleChangeTheme(newTheme); + if (userdata?.org_status?.includes("integration_partner")){ + handleChangeTheme(newTheme); + }else { + handleUpdateTheme(newTheme); + } }} aria-label="theme" style={{display: 'flex', justifyContent: "center", marginBottom: 10, }} From 3544562eea6e4150a389424fa4159675c1059ff1 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 20 May 2025 14:12:54 +0530 Subject: [PATCH 170/200] Fix - hide toast pop up while changing theme --- frontend/src/components/Branding.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index 649c509a..92e434e5 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -414,9 +414,6 @@ const Branding = (props) => { if (changingTheme === true) { return } - - - toast.info("Changing theme to " + newTheme + ". Please wait a moment.") setChangingTheme(true) handleEditOrg(newTheme); }} From 1eaad4e9bebad56c74b41d5747651aef27668d68 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 20 May 2025 14:17:38 +0530 Subject: [PATCH 171/200] remove brading from userinfo --- backend/go-app/main.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index a03f7d28..06021b6b 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1069,8 +1069,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { activatedAppIds = append(activatedAppIds, app.ID) } - userInfo.ActiveOrg.Branding = org.Branding - returnValue := shuffle.HandleInfo{ Success: true, Username: userInfo.Username, From 065f6f933205fcbc663ebb613e400119c49cea0a Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 20 May 2025 18:48:52 +0200 Subject: [PATCH 172/200] Minor fixes for frontend on shuffle with no internet --- frontend/src/App.jsx | 2 +- frontend/src/views/AdminSetup.jsx | 12 +++++++++--- frontend/src/views/LoginPage.jsx | 7 ++++++- frontend/src/views/LoginPageOld.jsx | 8 +++++++- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index fa3ac7ea..caa9e1cb 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -463,7 +463,7 @@ const App = (message, props) => { { .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - setLoginInfo(responseJson["reason"]); + setLoginInfo(responseJson["reason"]) + + if (responseJson?.reason?.toLowerCase().includes("connection refused")) { + navigate("/loginsetup") + } + } else { if (responseJson.reason === "redirect") { setTimeout(() => { - window.location.pathname = "/login"; + window.location.pathname = "/login" }, 2500) } + } }) ) @@ -111,7 +117,7 @@ const AdminAccount = (props) => { if (responseJson["success"] === false) { setLoginInfo(responseJson["reason"]); } else { - setLoginInfo("Successful register :)"); + setLoginInfo("Successful register! Redirecting in a moment..."); setTimeout(() => { window.location.pathname = "/login"; diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index ccba780d..26bf6782 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -361,7 +361,7 @@ const LoginPage = props => { console.log("Should login instead of register!") setRegister(!register) } else { - console.log("Path: " + path, "Register: " + register) + //console.log("Path: " + path, "Register: " + register) } } @@ -468,6 +468,11 @@ const LoginPage = props => { response.json().then((responseJson) => { if (responseJson["success"] === false) { setLoginInfo(responseJson["reason"]); + + if (responseJson?.reason?.toLowerCase().includes("connection refused")) { + navigate("/loginsetup") + } + } else { if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { setSSOUrl(responseJson.sso_url); diff --git a/frontend/src/views/LoginPageOld.jsx b/frontend/src/views/LoginPageOld.jsx index 541cd903..aaee73d6 100755 --- a/frontend/src/views/LoginPageOld.jsx +++ b/frontend/src/views/LoginPageOld.jsx @@ -85,7 +85,13 @@ const LoginDialog = (props) => { .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - setLoginInfo(responseJson["reason"]); + setLoginInfo(responseJson["reason"]) + + if (responseJson?.reason?.toLowerCase().includes("connection refused")) { + setLoginViewLoading(true) + start() + } + } else { if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { From af6db1d43285e70231d038f69a4293f82b39bbc9 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Wed, 21 May 2025 22:14:07 +0530 Subject: [PATCH 173/200] fix: requesting backend if we cannot find the executionId on cache --- functions/onprem/worker/worker.go | 92 +++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 16 deletions(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 80aa5e56..6c2681eb 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -2336,6 +2336,50 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar { } return envVars } +func getWorkerBackendExecution(auth string, executionId string) (*shuffle.WorkflowExecution, error) { + backendUrl := os.Getenv("BASE_URL") + if len(backendUrl) == 0 { + backendUrl = "http://shuffle-backend:5001" + } + + var workflowExecution *shuffle.WorkflowExecution + + streamResultUrl := fmt.Sprintf("%s/api/v1/streams/results", backendUrl) + topClient := shuffle.GetExternalClient(backendUrl) + requestData := shuffle.ActionResult { + Authorization: auth, + ExecutionId: executionId, + } + + data, err := json.Marshal(requestData) + if err != nil { + return workflowExecution, err + } + + req, err := http.NewRequest( + "POST", + streamResultUrl, + bytes.NewBuffer([]byte(data)), + ) + + newresp, err := topClient.Do(req) + if err != nil { + return workflowExecution, err + } + + defer newresp.Body.Close() + body, err := ioutil.ReadAll(newresp.Body) + if err != nil { + return workflowExecution, err + } + + err = json.Unmarshal(body, &workflowExecution) + if err != nil { + return workflowExecution, err + } + + return workflowExecution, nil +} func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { if request.Body == nil { @@ -2380,10 +2424,14 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) if err != nil { - log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err) - resp.WriteHeader(500) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId))) - return + log.Printf("[WARNING][%s] Failed to find execution in cache requesting backend (1): %s", actionResult.ExecutionId, err) + workflowExecution, err = getWorkerBackendExecution(actionResult.Authorization, actionResult.ExecutionId) + if err != nil { + log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err) + resp.WriteHeader(500) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId))) + return + } } if workflowExecution.Authorization != actionResult.Authorization { @@ -2429,10 +2477,14 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl //log.Printf("[DEBUG][%s] IN WORKFLOWEXECUTION SUB!", actionResult.ExecutionId) workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) if err != nil { - log.Printf("[ERROR] Failed getting execution cache: %s", err) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) - return + log.Printf("[WARNING][%s] Failed to find execution in cache requesting backend (2): %s", actionResult.ExecutionId, err) + workflowExecution, err = getWorkerBackendExecution(actionResult.Authorization, actionResult.ExecutionId) + if err != nil { + log.Printf("[ERROR] Failed getting execution cache: %s", err) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) + return + } } resultLength := len(workflowExecution.Results) @@ -2483,10 +2535,14 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl if strings.Contains(fmt.Sprintf("%s", err), "Rerun this transaction") { workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) if err != nil { - log.Printf("[ERROR][%s] Failed getting execution cache (2): %s", workflowExecution.ExecutionId, err) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution (2)"}`))) - return + log.Printf("[WARNING][%s] Failed to find execution in cache requesting backend (3): %s", actionResult.ExecutionId, err) + workflowExecution, err = getWorkerBackendExecution(actionResult.Authorization, actionResult.ExecutionId) + if err != nil { + log.Printf("[ERROR][%s] Failed getting execution cache (2): %s", workflowExecution.ExecutionId, err) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution (2)"}`))) + return + } } resultLength = len(workflowExecution.Results) @@ -2796,10 +2852,14 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { ctx := context.Background() workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) if err != nil { - log.Printf("[INFO] Failed getting execution (streamresult) %s: %s", actionResult.ExecutionId, err) - resp.WriteHeader(400) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) - return + log.Printf("[WARNING][%s] Failed to find execution in cache requesting backend (4): %s", actionResult.ExecutionId, err) + workflowExecution, err = getWorkerBackendExecution(actionResult.Authorization, actionResult.ExecutionId) + if err != nil { + log.Printf("[ERROR] Failed getting execution (streamresult) %s: %s", actionResult.ExecutionId, err) + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) + return + } } // Authorization is done here From 0344cc789b8e1d4427ed9a8efcdff7550a1b9f33 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Wed, 21 May 2025 22:22:17 +0530 Subject: [PATCH 174/200] handling bad status code --- functions/onprem/worker/worker.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 6c2681eb..cde2484c 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -2368,6 +2368,10 @@ func getWorkerBackendExecution(auth string, executionId string) (*shuffle.Workfl } defer newresp.Body.Close() + if newresp.StatusCode != 200 { + return workflowExecution, errors.New(fmt.Sprintf("Got bad status code from backend %d", newresp.StatusCode)) + } + body, err := ioutil.ReadAll(newresp.Body) if err != nil { return workflowExecution, err From 6264150592409aed3a0390a6c2da75928c9e9878 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Thu, 22 May 2025 14:26:13 +0530 Subject: [PATCH 175/200] remove extra api for changing theme --- backend/go-app/main.go | 1 - frontend/src/components/LeftSideBar.jsx | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 06021b6b..2e1cf238 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5103,7 +5103,6 @@ func initHandlers() { r.HandleFunc("/api/v1/users/{key}/get2fa", shuffle.HandleGet2fa).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/{key}/set2fa", shuffle.HandleSet2fa).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users", shuffle.HandleGetUsers).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/users/{userid}/theme", shuffle.HandleSetUserTheme).Methods("POST", "OPTIONS") // General - duplicates and old. r.HandleFunc("/api/v1/getusers", shuffle.HandleGetUsers).Methods("GET", "OPTIONS") diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index db93754b..39fce780 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -450,15 +450,16 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }; const handleUpdateTheme = (newTheme) => { - + const data = { + "user_id": userdata?.id, "theme": newTheme, } - const url = globalUrl + `/api/v1/users/${userdata?.id}/theme`; + const url = globalUrl + `/api/v1/users/updateuser`; fetch(url, { mode: "cors", - method: "POST", + method: "PUT", body: JSON.stringify(data), credentials: "include", crossDomain: true, From c0aaebaff603a65acb5b0d676892649f94d13a3e Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 23 May 2025 12:10:38 +0200 Subject: [PATCH 176/200] Synced over bugfixes and theme control --- frontend/src/components/Branding.jsx | 7 +- frontend/src/components/LeftSideBar.jsx | 46 +- frontend/src/components/Priorities.jsx | 2441 ++++++++++++----- .../src/components/ShuffleCodeEditor1.jsx | 10 +- frontend/src/views/AngularWorkflow.jsx | 18 +- frontend/src/views/LoginPage.jsx | 61 +- frontend/src/views/SettingsPage.jsx | 7 +- frontend/src/views/Workflows2.jsx | 6 +- 8 files changed, 1898 insertions(+), 698 deletions(-) diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index 649c509a..40a76846 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -39,7 +39,7 @@ const Branding = (props) => { const theme = getTheme(themeMode, brandColor) const [selectedBrandColor, setSelectedBrandColor] = useState(theme?.palette?.main || "#FF8544") const [selectedBrandName, setSelectedBrandName] = useState(selectedOrganization?.branding?.brand_name || "") - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const [isLoading,setIsLoading] = useState(false); const handleEditOrg = (joinStatus) => { @@ -391,7 +391,7 @@ const Branding = (props) => {
- { integrationPartner ? ( + {integrationPartner ? ( <> @@ -414,9 +414,6 @@ const Branding = (props) => { if (changingTheme === true) { return } - - - toast.info("Changing theme to " + newTheme + ". Please wait a moment.") setChangingTheme(true) handleEditOrg(newTheme); }} diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 2d9bad44..69a9b29e 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -403,7 +403,6 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { const handleChangeTheme = (newTheme) => { - toast.info("Changing theme to " + newTheme + " - please wait!"); const data = { "org_id": userdata?.active_org?.id, @@ -449,6 +448,38 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }); }; + + const handleUpdateTheme = (newTheme) => { + + const data = { + "user_id": userdata?.id, + "theme": newTheme, + } + + const url = globalUrl + `/api/v1/users/updateuser`; + fetch(url, { + mode: "cors", + method: "PUT", + body: JSON.stringify(data), + credentials: "include", + crossDomain: true, + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }).then((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === false) { + toast("Failed updating theme: ", responseJson.reason); + } else { + handleThemeChange(newTheme); + setCurrentSelectedTheme(newTheme); + } + }) + ).catch((error) => { + console.log("Error changing theme: ", error); + }); + }; const removeCookie = (name, path = "/") => { @@ -487,6 +518,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { console.error("Logout error:", error); }); }; + console.log("userdata: ", userdata); const avatarMenu = ( { }, }} > - {userdata && ( + {userdata && (userdata?.org_status?.includes("integration_partner") && userdata?.org_status?.includes("sub_org")) ? null : ( <> { if (newTheme === currentSelectedTheme) { return; } - handleChangeTheme(newTheme); + if (userdata?.org_status?.includes("integration_partner")){ + handleChangeTheme(newTheme); + }else { + handleUpdateTheme(newTheme); + } }} aria-label="theme" style={{display: 'flex', justifyContent: "center", marginBottom: 10, }} @@ -1648,7 +1684,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { + + + + ) : null + + const modalView = notificationWorkflowModal ? ( + { + setNotificationWorkflowModal(false); + }} + > + + +
+ {`Configure ${selectedAppDetails.name} workflow`} +
+
+ + + {console.log("len Selected app details: ", selectedAppDetails)} + {(selectedAppDetails.authentication_data || (selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false) || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? + <> + + {true || (selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? "No authentication required" : + <> + + Pick an authentication method from the list + + + Available authentications + + } + + + + Provide additional required details: + + { + setTextFieldOneValue(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} /> + { + setTextFieldValue(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} /> + + : + <> + 0) ? false : true} + // // setAuthenticationModalOpen={false} + selectedApp={{ ...selectedAppDetails, authentication: selectedAppDetails.auth_config }} + // getAppAuthentication={selectedAppDetails.name} + /> + + } + + + + + +
+
+ ) : null + + + const checkIfAlreadyGenerated = async (appList, workflows) => { // fixxxxxxxxxxxxxxxxxxxxx + + var workflowName = workflows.find(workflow => workflow.id === notificationWorkflow) + if (workflowName) { + workflowName = workflowName.name + } + else { + console.log("no workflow set") + return + } + if (workflowName) { + const parts = workflowName.split(' '); + console.log("parts", parts) + if (parts[0].toString() === "[GENERATED]" && parts.length > 1) { + console.log("parts1", parts[1]) + if ((appList.includes(parts[1]))) { + console.log("workflow already generated") + setGeneatedWorkflow({ "app_name": parts[1] }) + } + } + } + else { + return + } + } + + const renderChips = useCallback(() => { + const appList = Object.values(notificationAppsDetails); + return ( + + + {appList.map((app) => ( + { + console.log(`Clicked ${app.name}`) + console.log("app: ", app) + setSelectedAppDetails(app) + if (app.authentication_data && app.authentication_data.length > 0) { //fixxxxxxxx + console.log("authdata: ", app.authentication_data[0]) + setSelectedAuth(app.authentication_data[app.authentication_data.length - 1].id) + } + setNotificationWorkflowModal(true) + // getAppAuth(app.name) + console.log("selectedAppDEtails", selectedAppDetails) + }} + avatar={{app.name}} + /> + ))} + + + + + + Want access to more templates? + + Set up app authentication + + to unlock additional workflow options. + + + + ); + }, [notificationAppsDetails]) + useEffect(() => { getFramework() @@ -83,7 +1224,7 @@ const Priorities = memo((props) => { setSelectedExecutionId(execution_id) //toast.info("Execution-related notifications are highlighted.") - } + } if (workflow !== null) { setSelectedWorkflow(workflow) @@ -97,7 +1238,7 @@ const Priorities = memo((props) => { return } - if(workflows?.length === 0) { + if (workflows?.length === 0) { getAvailableWorkflows() } @@ -107,7 +1248,7 @@ const Priorities = memo((props) => { }, [selectedOrganization]) if (userdata === undefined || userdata === null) { - return + return } const getFramework = () => { @@ -119,67 +1260,67 @@ const Priorities = memo((props) => { }, credentials: "include", }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for framework!"); - } - - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === false) { - setAppFramework({}) - if (responseJson.reason !== undefined) { - //toast("Failed loading: " + responseJson.reason) - } else { - //toast("Failed to load framework for your org.") + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); } - } else { - setAppFramework(responseJson) - } - }) - .catch((error) => { - console.log("err in framework: ", error.toString()); - }) + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + setAppFramework({}) + if (responseJson.reason !== undefined) { + //toast("Failed loading: " + responseJson.reason) + } else { + //toast("Failed to load framework for your org.") + } + } else { + setAppFramework(responseJson) + } + }) + .catch((error) => { + console.log("err in framework: ", error.toString()); + }) } - const clearNotifications = () => { - // Don't really care about the logout + const clearNotifications = () => { + // Don't really care about the logout - toast("Clearing notifications") - 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"); - } + toast("Clearing notifications") + 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) { - // Reload the UI - const newNotifications = notifications.map((notification) => { - notification.read = true - return notification - }) + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + // Reload the UI + const newNotifications = notifications.map((notification) => { + notification.read = true + return notification + }) - setNotifications(newNotifications) - setShowRead(true) - } else { - toast("Failed dismissing notifications. Please try again later."); - } - }) - .catch((error) => { - console.log("error in notification dismissal: ", error); - //removeCookie("session_token", {path: "/"}) - }); - }; + setNotifications(newNotifications) + setShowRead(true) + } else { + toast("Failed dismissing notifications. Please try again later."); + } + }) + .catch((error) => { + console.log("error in notification dismissal: ", error); + //removeCookie("session_token", {path: "/"}) + }); + }; const dismissNotification = (alert_id, disabled) => { var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread` @@ -189,82 +1330,82 @@ const Priorities = memo((props) => { notificationurl += "?disabled=false" } - fetch(notificationurl , { - 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) { - // Mark current one as read - var newNotifications = notifications.map((notification) => { - if (notification.id === alert_id) { - notification.read = true + fetch(notificationurl, { + credentials: "include", + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); } - return notification + return response.json(); }) + .then(function (responseJson) { + if (responseJson.success === true) { + // Mark current one as read + var newNotifications = notifications.map((notification) => { + if (notification.id === alert_id) { + notification.read = true + } + + return notification + }) - if (disabled === true) { - toast("Notification disabled, and will not be shown again.") + if (disabled === true) { + toast("Notification disabled, and will not be shown again.") - newNotifications = newNotifications.map((notification) => { - if (notification.id === alert_id) { - notification.ignored = true + newNotifications = newNotifications.map((notification) => { + if (notification.id === alert_id) { + notification.ignored = true + } + + return notification + }) + + console.log("NEW NOTIFICATIONS: ", newNotifications); + } else if (disabled === false) { + toast("Notification re-enabled successfully") + + newNotifications = newNotifications.map((notification) => { + if (notification.id === alert_id) { + notification.ignored = false + } + + return notification + }) + + } else { + toast("Notification dismissed successfully") } - return notification - }) + //const newNotifications = notifications.filter( + // (data) => data.id !== alert_id + //) - console.log("NEW NOTIFICATIONS: ", newNotifications); - } else if (disabled === false) { - toast("Notification re-enabled successfully") + //console.log("NEW NOTIFICATIONS: ", newNotifications); - newNotifications = newNotifications.map((notification) => { - if (notification.id === alert_id) { - notification.ignored = false + if (setNotifications !== undefined && newNotifications !== undefined) { + setNotifications(newNotifications) } - - return notification - }) - - } else { - toast("Notification dismissed successfully") - } - - //const newNotifications = notifications.filter( - // (data) => data.id !== alert_id - //) - - //console.log("NEW NOTIFICATIONS: ", newNotifications); - - if (setNotifications !== undefined && newNotifications !== undefined) { - setNotifications(newNotifications) - } - } else { - toast("Failed dismissing notification. Please try again later."); - } - }) - .catch((error) => { - console.log("error in notification dismissal: ", error); - //removeCookie("session_token", {path: "/"}) - }) + } else { + toast("Failed dismissing notification. Please try again later."); + } + }) + .catch((error) => { + console.log("error in notification dismissal: ", error); + //removeCookie("session_token", {path: "/"}) + }) } - - const notificationWidth = "100%" + + const notificationWidth = "100%" const imagesize = 22 - const boxColor = "#86c142" + const boxColor = "#86c142" const getAvailableWorkflows = () => { @@ -354,134 +1495,168 @@ const Priorities = memo((props) => { } return ( -
-
-
- - Notification Workflow - - - The notification workflow triggers when an error occurs in one of your workflows. Each individual one will only start a workflow once every 2 minutes. You can point child org notifications into the parent org notification by choosing it in the list. - - -
+
+
+
+ + Notification Workflow + + + The notification workflow triggers when an error occurs in one of your workflows. Each individual one will only start a workflow once every 2 minutes. You can point child org notifications into the parent org notification by choosing it in the list. + - {workflows !== undefined && workflows !== null && workflows.length > 0 ? - { - setOpenNotification(true); - }} - onClose={() => { - setOpenNotification(false); - }} - freeSolo - //autoSelect - value={workflows?.find(w => w.id === notificationWorkflow) || null} - classes={{ inputRoot: classes.inputRoot }} - ListboxProps={{ - style: { - backgroundColor: theme.palette.surfaceColor, - color: theme.palette.text.primary, - borderRadius: theme.palette.borderRadius, - }, - }} - getOptionLabel={(option) => { - if ( - option === undefined || - option === null || - option.name === undefined || - option.name === null - ) { - return "No Workflow Selected"; - } + {modalView} + {/*{testWorkflowModal} */} +
+ {renderChips()} +
- const newname = ( - option.name.charAt(0).toUpperCase() + option.name.substring(1) - ).replaceAll("_", " "); - return newname; - }} - options={workflows} - fullWidth - style={{ - backgroundColor: theme.palette.textFieldStyle.backgroundColor, - borderRadius: theme.palette.textFieldStyle.borderRadius, - color: theme.palette.textFieldStyle.color, - height: 35, - marginBottom: 40, - }} - onChange={(event, newValue) => { - console.log("Found value: ", newValue) +
- var parsedinput = { target: { value: newValue } } - - // For variables - if (typeof newValue === 'string' && newValue.startsWith("$")) { - parsedinput = { - target: { - value: { - "name": newValue, - "id": newValue, - "actions": [], - "triggers": [], - } - } - } - } - - handleWorkflowSelectionUpdate(parsedinput) - }} - renderOption={(props, data, state) => { - if (data.id === workflow.id) { - data = workflow; - } - - return ( - - {data.image !== undefined && data.image !== null && data.image.length > 0 ? - {data.name} - : null} - - Choose {data.name} - - - } placement="bottom"> - 0 ? + { + setOpenNotification(true); + }} + onClose={() => { + setOpenNotification(false); + }} + freeSolo + //autoSelect + value={workflows?.find(w => w.id === notificationWorkflow) || null} + classes={{ inputRoot: classes.inputRoot }} + ListboxProps={{ + style: { backgroundColor: theme.palette.surfaceColor, - color: data.id === workflow.id ? "red" : theme.palette.text.primary, - borderBottom: data.id === "parent" ? "2px solid rgba(255,255,255,0.5)" : null - }} - value={data} - onClick={(e) => { - props.onMouseDown?.(null); - var parsedinput = { target: { value: data } } - handleWorkflowSelectionUpdate(parsedinput) - }} - > - {data.name} - - - ) - }} - renderInput={(params) => { - return ( - { + if ( + option === undefined || + option === null || + option.name === undefined || + option.name === null + ) { + return "No Workflow Selected"; + } + + const newname = ( + option.name.charAt(0).toUpperCase() + option.name.substring(1) + ).replaceAll("_", " "); + return newname; + }} + options={workflows} + fullWidth style={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor, - color: theme.palette.textFieldStyle.color, borderRadius: theme.palette.textFieldStyle.borderRadius, + color: theme.palette.textFieldStyle.color, height: 35, - fontSize: 16, - marginTop: "16px" + marginBottom: 40, }} + onChange={(event, newValue) => { + console.log("Found value: ", newValue) + + var parsedinput = { target: { value: newValue } } + + // For variables + if (typeof newValue === 'string' && newValue.startsWith("$")) { + parsedinput = { + target: { + value: { + "name": newValue, + "id": newValue, + "actions": [], + "triggers": [], + } + } + } + } + + handleWorkflowSelectionUpdate(parsedinput) + }} + renderOption={(props, data, state) => { + if (data.id === workflow.id) { + data = workflow; + } + + return ( + + {data.image !== undefined && data.image !== null && data.image.length > 0 ? + {data.name} + : null} + + Choose {data.name} + + + } placement="bottom"> + { + props.onMouseDown?.(null); + var parsedinput = { target: { value: data } } + handleWorkflowSelectionUpdate(parsedinput) + }} + > + {data.name} + + + ) + }} + renderInput={(params) => { + return ( + + ); + }} + /> + : + { borderRadius: 4, }, inputProps: { - ...params.inputProps, style: { height: "100%", boxSizing: "border-box", @@ -499,198 +1673,171 @@ const Priorities = memo((props) => { } }} - // label="Find a notification workflow" - variant="outlined" - placeholder="Select a notification workflow" + style={{ + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + color: theme.palette.textFieldStyle.color, + borderRadius: 4, + height: 35, + fontSize: 16, + marginBottom: 30 + }} + fullWidth={true} + type="name" + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="ID of the workflow to receive notifications" + value={notificationWorkflow} + onChange={(e) => { + setNotificationWorkflow(e.target.value); + }} /> - ); - }} - /> - : - { - setNotificationWorkflow(e.target.value); - }} - /> - } - {/*
+ {/*
{orgSaveButton}
*/} -
+
- {notificationWorkflow === undefined || notificationWorkflow === null || notificationWorkflow.length === 0 ? null : -
- + { + if (notificationWorkflow === "parent") { + toast.error("Can't open parent org's notification workflow from here.") + return + } + + window.open(`/workflows/${notificationWorkflow}?view=executions`, "_blank") + }} + > + + +
} - fetch(`${globalUrl}/api/v1/workflows/${notificationWorkflow}/execute`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "Accept": "application/json", - }, - credentials: "include", - body: JSON.stringify({ - "title": "Test Notification", - "description": "This is a test notification to check if the notification workflow is working correctly.", - "org_id": selectedOrganization.id, - "id": uuidv4(), - "reference_url": "/admin?type=test&admin_tab=notifications", - "created_at": Math.floor(new Date().getTime() / 1000), - "updated_at": Math.floor(new Date().getTime() / 1000), + Notifications ({ + notifications?.filter((notification) => showRead === true || notification.read === false).length + }) + + + Notifications help you find potential problems with your workflows and apps.  + + Learn more + + +
+
+ { + setShowRead(!showRead); + }} + />  Show read + {notifications !== undefined && notifications !== null && notifications.length > 1 ? ( + + ) : null} +
+ + + + {clickedFromOrgTab ? null : } + + Suggestions + + Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company.
These range from simple configurations in Shuffle to Usecases you may have missed.  + + Learn more + +
+
+ { + setShowDismissed(!showDismissed); + }} + />  Show dismissed + {userdata.priorities === null || userdata.priorities === undefined || userdata.priorities.length === 0 ? + + No Suggestions found + + : + userdata.priorities.map((priority, index) => { + if (showDismissed === false && priority.active === false) { + return null + } + + return ( + + ) }) - }) - .then((response) => { - if (response.status === 200) { - toast.success("Test notification sent successfully.") - } else { - toast.error("Failed to send test notification. Please contact support if this persists") - } - }).catch((error) => { - toast.error("Failed to send test notification (2). Please contact support if this persists") - }) - }}> - Send test notification - - { - if (notificationWorkflow === "parent") { - toast.error("Can't open parent org's notification workflow from here.") - return - } - - window.open(`/workflows/${notificationWorkflow}?view=executions`, "_blank") - }} - > - - -
- } - - Notifications ({ - notifications?.filter((notification) => showRead === true || notification.read === false).length - }) - - - Notifications help you find potential problems with your workflows and apps.  - - Learn more - - -
-
- { - setShowRead(!showRead); - }} - />  Show read - {notifications !== undefined && notifications !== null && notifications.length > 1 ? ( - - ) : null} -
- - - - {clickedFromOrgTab? null : } - - Suggestions - - Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company.
These range from simple configurations in Shuffle to Usecases you may have missed.  - - Learn more - -
-
- { - setShowDismissed(!showDismissed); - }} - />  Show dismissed - {userdata.priorities === null || userdata.priorities === undefined || userdata.priorities.length === 0 ? - - No Suggestions found - - : - userdata.priorities.map((priority, index) => { - if (showDismissed === false && priority.active === false) { - return null } - - return ( - - ) - }) - } -
-
+
+
) }) @@ -699,15 +1846,15 @@ export default Priorities; const NotificationItem = memo((props) => { - const {data, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification} = props + const { data, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification } = props var image = ""; var orgName = ""; var orgId = ""; const { themeMode, brandColor } = useContext(Context); - const theme = getTheme(themeMode, brandColor); - - var highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow) + const theme = getTheme(themeMode, brandColor); + + var highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow) if (!highlighted && highlightKMS) { if (data.title !== undefined && data.title !== null && data.title.toLowerCase().includes("kms")) { @@ -719,45 +1866,45 @@ const NotificationItem = memo((props) => { } if (userdata.orgs !== undefined) { - const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]); - if (foundOrg !== undefined && foundOrg !== null) { - //position: "absolute", bottom: 5, right: -5, - const imageStyle = { - width: imagesize, - height: imagesize, - pointerEvents: "none", - marginLeft: - data.creator_org !== undefined && data.creator_org.length > 0 - ? 20 - : 0, - borderRadius: 10, - border: - foundOrg.id === userdata.active_org.id - ? `3px solid ${boxColor}` - : null, - cursor: "pointer", - marginRight: 10, - }; + const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]); + if (foundOrg !== undefined && foundOrg !== null) { + //position: "absolute", bottom: 5, right: -5, + const imageStyle = { + width: imagesize, + height: imagesize, + pointerEvents: "none", + marginLeft: + data.creator_org !== undefined && data.creator_org.length > 0 + ? 20 + : 0, + borderRadius: 10, + border: + foundOrg.id === userdata.active_org.id + ? `3px solid ${boxColor}` + : null, + cursor: "pointer", + marginRight: 10, + }; - image = - foundOrg.image === "" ? ( - {foundOrg.name} - ) : ( - {foundOrg.name} {}} - /> - ); + image = + foundOrg.image === "" ? ( + {foundOrg.name} + ) : ( + {foundOrg.name} { }} + /> + ); - orgName = foundOrg.name; - orgId = foundOrg.id; - } + orgName = foundOrg.name; + orgId = foundOrg.id; + } } return ( @@ -776,185 +1923,185 @@ const NotificationItem = memo((props) => { backgroundColor: theme.palette.cardHoverColor, }, }} - > -
- {data.amount === 1 && data.read === false ? - - : null} - {data.ignored === true ? - - : null} - {data.read === false ? - - : - - } - - {data.title} - -
- - {data.image !== undefined && data.image !== null && data.image.length > 0 ? - {data.title} - : - null - } - - {data.description} - -
- - - - {data.read === false ? ( - - ) : null} - - - - - +
+ {data.amount === 1 && data.read === false ? + + : null} + {data.ignored === true ? + + : null} + {data.read === false ? + + : + + } + + {data.title} + +
- 0 ? + {data.title} + : + null + } + + {data.description} + +
+ + - }} - > - First seen:{" "} - {new Date(data.created_at * 1000).toISOString().slice(0, 19)} - + {data.read === false ? ( + + ) : null} - - Last seen:{" "} - {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} - + + + + - - Times seen: {data.amount} - -
+ + }} + > + First seen:{" "} + {new Date(data.created_at * 1000).toISOString().slice(0, 19)} + + + + Last seen:{" "} + {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} + + + + Times seen: {data.amount} + +
+ + ); }) -const NotificationComponent = memo(({notifications, showRead, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification}) => { +const NotificationComponent = memo(({ notifications, showRead, selectedExecutionId, selectedWorkflow, highlightKMS, userdata, imagesize, boxColor, clickedFromOrgTab, notificationWidth, dismissNotification }) => { - return( + return (
{notifications === null || notifications === undefined || notifications?.length === 0 ? ( - null - ) : -
- {notifications?.map((notification, index) => { - if (showRead === false && notification.read === true) { - return null - } + null + ) : +
+ {notifications?.map((notification, index) => { + if (showRead === false && notification.read === true) { + return null + } - return ( - - ) - })} -
- } + return ( + + ) + })} +
+ }
) }) diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index 33b37483..3132034f 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -132,7 +132,7 @@ const CodeEditor = (props) => { fieldname, contentLoading, editorData, - handleSubflowParamChange, + handleTriggerParamChange, setAiQueryModalOpen, fullScreenMode, environment, @@ -212,7 +212,7 @@ const CodeEditor = (props) => { const triggerField = searchParams.get('trigger_field'); const triggerName = searchParams.get('trigger_name'); const conditionId = searchParams.get('condition_id'); - const conditionField = searchParams.get('field'); + const conditionField = searchParams.get('condition_field'); useEffect(() => { if (actionId === undefined || actionId === null) { @@ -251,7 +251,7 @@ const CodeEditor = (props) => { setSelectedCondition(condition); // Update available variables when condition changes updateAvailableVariables(actionlist); - }, [conditionId, fieldName]) + }, [conditionId, conditionField]) // Extract variable updating logic into a separate function const updateAvailableVariables = (actionlist) => { @@ -2588,7 +2588,7 @@ const CodeEditor = (props) => { // Handle condition fields if (conditionField !== null && handleConditionFieldChange !== undefined) { - handleConditionFieldChange(conditionField, fieldName, fixedcodedata); + handleConditionFieldChange(conditionField, fixedcodedata); } // Handle action fields else if (actionId !== undefined && actionId !== null && actionId.length > 0) { @@ -2596,7 +2596,7 @@ const CodeEditor = (props) => { } // Handle trigger fields else if (triggerId !== undefined && triggerId !== null && triggerId.length > 0) { - handleSubflowParamChange(triggerId, triggerField, fixedcodedata) + handleTriggerParamChange(triggerId, triggerField, fixedcodedata) } setExpansionModalOpen(false) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index db5111bd..95b3e462 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -13032,7 +13032,7 @@ const AngularWorkflow = (defaultprops) => { event.preventDefault() setExpansionModalOpen(true) setActiveDialog("codeeditor") - navigate(`?condition_id=${data.id}&field=${data.name}`) + navigate(`?condition_id=${data.id}&condition_field=${data.name}`) setEditorData({ "name": data.name, "value": data.value || "", @@ -13117,9 +13117,9 @@ const AngularWorkflow = (defaultprops) => { // Update the field value based on type if (type === "source") { - handleConditionFieldChange("source", "value", toComplete); + handleConditionFieldChange("source", toComplete); } else if (type === "destination") { - handleConditionFieldChange("destination", "value", toComplete); + handleConditionFieldChange("destination", toComplete); } handleMenuClose(); @@ -13996,7 +13996,7 @@ const AngularWorkflow = (defaultprops) => { /> - const handleConditionFieldChange = (fieldType, fieldName, value) => { + const handleConditionFieldChange = (fieldType, value) => { if (fieldType === "source") { setSourceValue({ ...sourceValue, @@ -15197,7 +15197,7 @@ const AngularWorkflow = (defaultprops) => { } ] - const handleSubflowParamChange = (triggerId, triggerField, newData) => { + const handleTriggerParamChange = (triggerId, triggerField, newData) => { var updateFail = "" if (workflow !== undefined && workflow !== null) { @@ -17336,9 +17336,13 @@ const AngularWorkflow = (defaultprops) => { fullWidth rows="4" multiline - defaultValue={selectedTrigger.parameters[0]?.value} + value={selectedTriggerValue || ""} color="primary" placeholder="" + onChange={(e) => { + setLastSaved(false) + setSelectedTriggerValue(e.target.value) + }} onBlur={(e) => { setLastSaved(false) setTriggerTextInformationWrapper(e.target.value); @@ -25472,7 +25476,7 @@ const AngularWorkflow = (defaultprops) => { // selectedTrigger={selectedTrigger} aiSubmit={aiSubmit} toolsAppId={toolsApp.id} - handleSubflowParamChange={handleSubflowParamChange} + handleTriggerParamChange={handleTriggerParamChange} codedata={editorData.value} setcodedata={setcodedata} selectedEdge={selectedEdge} diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 26bf6782..b2cb9426 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -314,12 +314,15 @@ const LoginPage = props => { }, }) - if (serverside !== true) { - const tmpMessage = new URLSearchParams(window.location.search).get("message") - if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) { - setMessage(tmpMessage) + useEffect(() => { + if (serverside !== true) { + const tmpMessage = new URLSearchParams(window.location.search).get("message") + if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) { + setMessage(tmpMessage) + toast(tmpMessage) + } } - } + }, []) if (document !== undefined) { if (register) { @@ -444,13 +447,17 @@ const LoginPage = props => { } if (isLoggedIn === true && serverside !== true) { - const tmpView = new URLSearchParams(window.location.search).get("view") - if (tmpView !== undefined && tmpView !== null && tmpView === "pricing") { - window.location.pathname = "/pricing" - return - } else if (tmpView !== undefined && tmpView !== null) { - window.location.pathname = tmpView - return + const tmpView = new URLSearchParams(window.location.search).get("view"); + if (tmpView !== undefined && tmpView !== null) { + let pathOnly = tmpView.split("?")[0]; + if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly; + + if (pathOnly === "/pricing" || pathOnly === "admin") { + window.location.replace(pathOnly + window.location.search); + } else { + window.location.replace(pathOnly); + } + return; } window.location.pathname = "/workflows" @@ -575,20 +582,17 @@ const LoginPage = props => { setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) } - const tmpView = new URLSearchParams(window.location.search).get("view") + const tmpView = new URLSearchParams(window.location.search).get("view"); if (tmpView !== undefined && tmpView !== null) { - //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}` - // Check if slash in the url + let pathOnly = tmpView.split("?")[0]; + if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly; - var newUrl = `/${tmpView}` - if (tmpView.startsWith("/")) { - newUrl = `${tmpView}` + if (pathOnly === "/pricing" || pathOnly === "admin") { + window.location.replace(pathOnly + window.location.search); + } else { + window.location.replace(pathOnly); } - - console.log("Found url: ", newUrl) - - window.location.pathname = newUrl - return + return; } console.log("LOGIN DATA: ", responseJson) @@ -647,9 +651,14 @@ const LoginPage = props => { const tmpView = new URLSearchParams(window.location.search).get("view") if (tmpView !== undefined && tmpView !== null) { - //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}` - const newUrl = `/${tmpView}` - window.location.pathname = newUrl + let pathOnly = tmpView.split("?")[0]; + if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly; + + if (pathOnly === "/pricing" || pathOnly === "admin") { + window.location.replace(pathOnly + window.location.search); + } else { + window.location.replace(pathOnly); + } return } diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx index f10dbfb7..ec5ba658 100755 --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -458,7 +458,12 @@ const Settings = (props) => { if (responseJson["success"] === false) { setPasswordFormMessage(responseJson["reason"]); } else { - toast("Changed password!"); + var reason = "" + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + reason += responseJson.reason + } + + toast.success("Changed password! " + reason); setPasswordFormMessage(""); } }) diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 1655c901..c059f991 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -5071,7 +5071,7 @@ const Workflows2 = (props) => { //isLoaded && isLoggedIn && workflowDone ? ( const loadedCheck = - workflowDone ? ( + workflowDone && isLoaded ? (
{/* @@ -5134,7 +5134,9 @@ const Workflows2 = (props) => { }} > - Loading Workflows + + Loading Workflows and Apps +
); From 425c4d080826b8e2a8dd55928d0af14a52b75e9b Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 27 May 2025 23:31:19 +0200 Subject: [PATCH 177/200] Changed Algolia public API Key --- backend/go-app/main.go | 5 +--- frontend/src/components/AppAuthTab.jsx | 2 +- frontend/src/components/AppGrid.jsx | 2 +- frontend/src/components/AppModal.jsx | 2 +- frontend/src/components/AppSearch1.jsx | 2 +- frontend/src/components/Appsearch.jsx | 2 +- frontend/src/components/Billing.jsx | 8 +++--- frontend/src/components/CloudSyncTab.jsx | 21 +++++++++++++-- frontend/src/components/ConfigureWorkflow.jsx | 2 +- frontend/src/components/CreatorGrid.jsx | 2 +- frontend/src/components/DocsGrid.jsx | 2 +- frontend/src/components/EditWorkflow.jsx | 6 ++--- frontend/src/components/LeftSideBar.jsx | 2 +- frontend/src/components/SearchData.jsx | 2 +- frontend/src/components/WorkflowGrid.jsx | 2 +- frontend/src/components/Workflowsearch.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 27 +++++++++++++------ frontend/src/views/ApiExplorerWrapper.jsx | 2 +- frontend/src/views/AppExplorer.jsx | 6 ++--- frontend/src/views/Apps.jsx | 6 ++--- frontend/src/views/Apps2.jsx | 2 +- frontend/src/views/Workflows2.jsx | 4 +-- 22 files changed, 69 insertions(+), 42 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 60f162b4..1aaa954e 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -447,7 +447,7 @@ func checkGitProxy(cloneOptions *git.CloneOptions) *git.CloneOptions { func createNewUser(username, password, role, apikey string, org shuffle.OrgMini) error { // Returns false if there is an issue // Use this for register - err := shuffle.CheckPasswordStrength(password) + err := shuffle.CheckPasswordStrength(username, password) if err != nil { log.Printf("[WARNING] Bad password strength: %s", err) return err @@ -460,8 +460,6 @@ func createNewUser(username, password, role, apikey string, org shuffle.OrgMini) } ctx := context.Background() - //users, err := FindUser(ctx context.Context, username string) ([]User, error) { - users, err := shuffle.FindUser(ctx, strings.ToLower(strings.TrimSpace(username))) if err != nil && len(users) == 0 { log.Printf("[WARNING] Failed getting user %s: %s", username, err) @@ -486,7 +484,6 @@ func createNewUser(username, password, role, apikey string, org shuffle.OrgMini) newUser.Active = true newUser.Orgs = []string{org.Id} - // FIXME - Remove this later if role == "admin" { newUser.Role = "admin" newUser.Roles = []string{"admin"} diff --git a/frontend/src/components/AppAuthTab.jsx b/frontend/src/components/AppAuthTab.jsx index 643aa8dc..c5b5c57f 100644 --- a/frontend/src/components/AppAuthTab.jsx +++ b/frontend/src/components/AppAuthTab.jsx @@ -66,7 +66,7 @@ import { Context } from '../context/ContextApi.jsx'; const searchClient = algoliasearch( "JNSS5CFDZZ", - "db08e40265e2941b9a7d8f644b6e5240" + "c8f882473ff42d41158430be09ec2b4e" ) const AppAuthTab = memo((props) => { diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index 8a5d9a18..e647085b 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -53,7 +53,7 @@ import { const searchClient = algoliasearch( "JNSS5CFDZZ", - "db08e40265e2941b9a7d8f644b6e5240" + "c8f882473ff42d41158430be09ec2b4e" ); //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") diff --git a/frontend/src/components/AppModal.jsx b/frontend/src/components/AppModal.jsx index 4b3b9516..38c8581f 100644 --- a/frontend/src/components/AppModal.jsx +++ b/frontend/src/components/AppModal.jsx @@ -35,7 +35,7 @@ import { Context } from '../context/ContextApi.jsx'; const searchClient = algoliasearch( "JNSS5CFDZZ", - "db08e40265e2941b9a7d8f644b6e5240" + "c8f882473ff42d41158430be09ec2b4e" );; const AppModal = ({ open, onClose, app, globalUrl, getApps}) => { diff --git a/frontend/src/components/AppSearch1.jsx b/frontend/src/components/AppSearch1.jsx index 3947f640..449ad925 100644 --- a/frontend/src/components/AppSearch1.jsx +++ b/frontend/src/components/AppSearch1.jsx @@ -13,7 +13,7 @@ import { InputAdornment, Typography, } from '@mui/material'; -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const Appsearch = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp, placeholder, diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index d13eb9a1..8fbb7e38 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -20,7 +20,7 @@ import { } from '@mui/material'; import aa from 'search-insights' -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const Appsearch = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props const { themeMode } = useContext(Context) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index b0e85c8d..af8bb78c 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -52,11 +52,13 @@ import { //import { useAlert import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; -import BillingStats from "./BillingStats.jsx"; +import BillingStats from "../components/BillingStats.jsx"; +import LicencePopup from "../components/LicencePopup.jsx"; import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" -import DeleteIcon from '@mui/icons-material/Delete'; + import { Context } from "../context/ContextApi.jsx"; -import LicencePopup from "./LicencePopup.jsx"; + +import DeleteIcon from '@mui/icons-material/Delete'; import { DataGrid } from "@mui/x-data-grid"; const Billing = memo((props) => { diff --git a/frontend/src/components/CloudSyncTab.jsx b/frontend/src/components/CloudSyncTab.jsx index d4aaa072..dd43bc4a 100644 --- a/frontend/src/components/CloudSyncTab.jsx +++ b/frontend/src/components/CloudSyncTab.jsx @@ -48,15 +48,21 @@ const CloudSyncTab = (props) => { const [, forceUpdate] = React.useState(); const itemColor = "white"; const isCloud = window?.location?.host === "localhost:3002" || window?.location?.host === "shuffler.io"; + const { themeMode, brandColor } = useContext(Context); const theme = getTheme(themeMode, brandColor); + useEffect(() => { getSettings(); }, []); + const GridItem = (props) => { const [expanded, setExpanded] = React.useState(false); const [showEdit, setShowEdit] = React.useState(false); const [newValue, setNewValue] = React.useState(-100); - const primary = props.data.primary; + var primary = props.data.primary + + const shownName = props.data.newname !== undefined && props.data.newname !== null && props.data.newname !== primary ? props.data.newname : primary + const secondary = props.data.secondary; const primaryIcon = props.data.icon; const secondaryIcon = props.data.active ? @@ -191,7 +197,7 @@ const CloudSyncTab = (props) => { {isCloud && userdata.support === true ? @@ -717,7 +723,9 @@ const CloudSyncTab = (props) => { {selectedOrganization.sync_features === undefined || selectedOrganization.sync_features === null ? + {[...Array(18)].map((_, i) => ( +
{ } const newkey = key.replaceAll("_", " "); + + // Rewrites to frontend names + var newname = newkey + if (newkey === "app executions") { + newname = "app runs" + } + const griditem = { primary: newkey, secondary: @@ -770,6 +785,8 @@ const CloudSyncTab = (props) => { data_collection: "None", active: item.active, icon: , + + newname: newname, }; return ( diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 8dea326c..b6f0c734 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -633,7 +633,7 @@ const ConfigureWorkflow = (props) => { if (aa !== undefined) { aa('init', { appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", + apiKey: "c8f882473ff42d41158430be09ec2b4e", }) const timestamp = new Date().getTime() diff --git a/frontend/src/components/CreatorGrid.jsx b/frontend/src/components/CreatorGrid.jsx index f3922e3e..96e9c059 100644 --- a/frontend/src/components/CreatorGrid.jsx +++ b/frontend/src/components/CreatorGrid.jsx @@ -37,7 +37,7 @@ import { AvatarGroup, } from "@mui/material" -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const CreatorGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, isHeader } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows diff --git a/frontend/src/components/DocsGrid.jsx b/frontend/src/components/DocsGrid.jsx index 7a32001c..d22beaa4 100644 --- a/frontend/src/components/DocsGrid.jsx +++ b/frontend/src/components/DocsGrid.jsx @@ -29,7 +29,7 @@ import { -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const DocsGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 8265f079..939b3998 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -226,8 +226,8 @@ const EditWorkflow = (props) => {
- - {newWorkflow ? "New" : "Editing"} workflow + + {newWorkflow ? "New" : "Editing"} Workflow {newWorkflow === true ? null : @@ -393,7 +393,7 @@ const EditWorkflow = (props) => {
- +
{ diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 69a9b29e..92f03b7e 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -518,7 +518,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { console.error("Logout error:", error); }); }; - console.log("userdata: ", userdata); + const avatarMenu = ( { const { serverside, globalUrl, userdata } = props let navigate = useNavigate(); diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index 5de17156..218cc060 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -24,7 +24,7 @@ import { import WorkflowPaper from "../components/WorkflowPaper.jsx" import WorkflowPaperNew from "../components/WorkflowPaperNew.jsx" -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const AppGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props diff --git a/frontend/src/components/Workflowsearch.jsx b/frontend/src/components/Workflowsearch.jsx index b8f4c022..1b342645 100644 --- a/frontend/src/components/Workflowsearch.jsx +++ b/frontend/src/components/Workflowsearch.jsx @@ -10,7 +10,7 @@ import algoliasearch from 'algoliasearch'; import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom'; import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@mui/material'; -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const WorkflowSearch = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, selectAble, } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 95b3e462..7526c9d7 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -438,7 +438,7 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); //const referenceUrl = "https://shuffler.io/functions/webhooks/" //const referenceUrl = window.location.origin+"/api/v1/hooks/" -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const AngularWorkflow = (defaultprops) => { const { globalUrl, setCookie, isLoggedIn, isLoaded, userdata, data_id, ReactGA, } = defaultprops; const {themeMode, supportEmail, brandColor} = useContext(Context) @@ -3573,6 +3573,12 @@ const AngularWorkflow = (defaultprops) => { if (curapp?.actions === undefined || curapp?.actions === null || curapp?.actions?.length === 0 || curapp?.actions?.length === 1) { loadAppConfig(curapp?.id, false, true) } + + if (key > 10) { + console.log("Breaking on 10 sideloads of total", responseJson.length) + break + + } } // Find app with ID "794e51c3c1a8b24b89ccc573a3defc47" (gmail) to force-break it, @@ -11856,7 +11862,7 @@ const AngularWorkflow = (defaultprops) => { if (queryID !== undefined && queryID !== null) { aa('init', { appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", + apiKey: "c8f882473ff42d41158430be09ec2b4e", }) const timestamp = new Date().getTime() @@ -18698,7 +18704,9 @@ const AngularWorkflow = (defaultprops) => { {originalWorkflow?.suborg_distribution === undefined || originalWorkflow?.suborg_distribution === null || originalWorkflow?.suborg_distribution?.length === 0 || originalWorkflow?.suborg_distribution.includes("none") ? - originalWorkflow?.parentorg_workflow !== undefined && originalWorkflow?.parentorg_workflow !== null && originalWorkflow?.parentorg_workflow.length > 0 || workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0 ? +
+ {originalWorkflow?.parentorg_workflow !== undefined && originalWorkflow?.parentorg_workflow !== null && originalWorkflow?.parentorg_workflow.length > 0 || workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0 ? + - : userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 1 && workflow?.id !== undefined && workflow?.id && workflow?.id?.length > 0 ? + + : null} + + {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 1 && workflow?.id !== undefined && workflow?.id && workflow?.id?.length > 0 ? - : null - + : null} +
: { if (queryID !== undefined && queryID !== null) { aa('init', { appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", + apiKey: "c8f882473ff42d41158430be09ec2b4e", }) const timestamp = new Date().getTime() diff --git a/frontend/src/views/ApiExplorerWrapper.jsx b/frontend/src/views/ApiExplorerWrapper.jsx index 45f9d762..d104d43d 100644 --- a/frontend/src/views/ApiExplorerWrapper.jsx +++ b/frontend/src/views/ApiExplorerWrapper.jsx @@ -50,7 +50,7 @@ import { green } from "../views/AngularWorkflow.jsx" const searchClient = algoliasearch( "JNSS5CFDZZ", - "db08e40265e2941b9a7d8f644b6e5240" + "c8f882473ff42d41158430be09ec2b4e" ) // Lazy loading of ApiExplorer component to reduce initial load time diff --git a/frontend/src/views/AppExplorer.jsx b/frontend/src/views/AppExplorer.jsx index c2b138a9..94bd3ed3 100644 --- a/frontend/src/views/AppExplorer.jsx +++ b/frontend/src/views/AppExplorer.jsx @@ -93,7 +93,7 @@ import aa from "search-insights"; // 2 = OpenAPI (Invalid) const searchClient = algoliasearch( "JNSS5CFDZZ", - "db08e40265e2941b9a7d8f644b6e5240" + "c8f882473ff42d41158430be09ec2b4e" ) const AppExplorer = (props) => { @@ -3996,7 +3996,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"; if (queryID !== undefined && queryID !== null) { aa("init", { appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", + apiKey: "c8f882473ff42d41158430be09ec2b4e", }); const timestamp = new Date().getTime(); @@ -4085,7 +4085,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"; if (queryID !== undefined && queryID !== null) { aa("init", { appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", + apiKey: "c8f882473ff42d41158430be09ec2b4e", }); const timestamp = new Date().getTime(); diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 8b4eadd4..684d8e9c 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -280,7 +280,7 @@ export const GetParsedPaths = (inputdata, basekey) => { return parsedValues; }; -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e") const Apps = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata, serverside, } = props; @@ -1305,7 +1305,7 @@ const Apps = (props) => { if (queryID !== undefined && queryID !== null) { aa("init", { appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", + apiKey: "c8f882473ff42d41158430be09ec2b4e", }); const timestamp = new Date().getTime(); @@ -2036,7 +2036,7 @@ const Apps = (props) => { if (queryID !== undefined && queryID !== null) { aa('init', { appId: "JNSS5CFDZZ", - apiKey: "db08e40265e2941b9a7d8f644b6e5240", + apiKey: "c8f882473ff42d41158430be09ec2b4e", }) const timestamp = new Date().getTime() diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index d054c232..6b062800 100644 --- a/frontend/src/views/Apps2.jsx +++ b/frontend/src/views/Apps2.jsx @@ -46,7 +46,7 @@ import AppCreationModal from "../components/AppCreationModal.jsx"; const searchClient = algoliasearch( "JNSS5CFDZZ", - "db08e40265e2941b9a7d8f644b6e5240" + "c8f882473ff42d41158430be09ec2b4e" ); // AppCard Component diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index c059f991..9c7f5466 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -111,7 +111,7 @@ import { removeQuery } from "../components/ScrollToTop.jsx"; import {green, yellow, red, grey } from "../views/AngularWorkflow.jsx" -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240"); +const searchClient = algoliasearch("JNSS5CFDZZ", "c8f882473ff42d41158430be09ec2b4e"); const svgSize = 24; const imagesize = 22; @@ -5071,7 +5071,7 @@ const Workflows2 = (props) => { //isLoaded && isLoggedIn && workflowDone ? ( const loadedCheck = - workflowDone && isLoaded ? ( + workflowDone ? (
{/* From 1281f254055e44b133c7f448ac67920aa679c438 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 29 May 2025 21:52:22 +0200 Subject: [PATCH 178/200] Autobuild backend pls :)) --- backend/go-app/go.mod | 4 ++-- backend/go-app/go.sum | 4 ++-- backend/go-app/main.go | 34 ++++++++++++++++++++++------------ 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 9accdf46..449a215a 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.8 //replace github.com/frikky/schemaless => ../../../schemaless //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi -replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared require ( cloud.google.com/go/datastore v1.20.0 @@ -22,7 +22,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.60 + github.com/shuffle/shuffle-shared v0.8.70 golang.org/x/crypto v0.37.0 google.golang.org/api v0.228.0 google.golang.org/grpc v1.71.1 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index e4b4e8e0..34ccd9f6 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -341,8 +341,8 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.58 h1:QzCKtQjuaozb+xyLkw6IjwqCNVWzpuJJnxkpXkLCP9M= -github.com/shuffle/shuffle-shared v0.8.58/go.mod h1:OLAwH/Ym4941Jn5DF1oZaq6iBpmjG2SNrTZ9Xqck5So= +github.com/shuffle/shuffle-shared v0.8.70 h1:Balbk7kIVUSgM9Lc+VJ09ZpaF18MlFxMPZdkPTD/+gU= +github.com/shuffle/shuffle-shared v0.8.70/go.mod h1:OLAwH/Ym4941Jn5DF1oZaq6iBpmjG2SNrTZ9Xqck5So= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 1aaa954e..657e4a1f 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1850,6 +1850,8 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { // return //} + log.Printf("[DEBUG] HOOKS: webhook callback: %s", request.URL.String()) + if request.Method != "POST" { request.Method = "POST" } @@ -1861,6 +1863,7 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { path := strings.Split(request.URL.String(), "/") if len(path) < 4 { + log.Printf("[DEBUG] HOOKS: Invalid webhook path: %s", request.URL.String()) resp.WriteHeader(403) resp.Write([]byte(`{"success": false}`)) return @@ -1876,7 +1879,7 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { if location[1] == "api" { if len(location) <= 4 { log.Printf("[INFO] Couldn't handle location. Too short in webhook: %d", len(location)) - resp.WriteHeader(401) + resp.WriteHeader(400) resp.Write([]byte(`{"success": false}`)) return } @@ -1893,6 +1896,8 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { } } + log.Printf("[DEBUG] HOOKS: Pre user agent check") + // Find user agent header userAgent := request.Header.Get("User-Agent") if strings.Contains(strings.ToLower(userAgent), "microsoftpreview") || strings.Contains(strings.ToLower(userAgent), "googlebot") { @@ -1915,8 +1920,8 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { //log.Printf("HookID: %s", hookId) hook, err := shuffle.GetHook(ctx, hookId) if err != nil { - log.Printf("[WARNING] Failed getting hook %s (callback): %s", hookId, err) - resp.WriteHeader(401) + log.Printf("[WARNING] HOOKS: Failed getting hook %s (callback): %s", hookId, err) + resp.WriteHeader(400) resp.Write([]byte(`{"success": false}`)) return } @@ -1928,21 +1933,21 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { //resp.WriteHeader(200) //resp.Write([]byte(`{"success": true}`)) if hook.Status == "stopped" { - log.Printf("[WARNING] Not running %s because hook status is stopped", hook.Id) - resp.WriteHeader(401) + log.Printf("[WARNING] HOOKS: Not running %s because hook status is stopped", hook.Id) + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The webhook isn't running. Is it running?"}`))) return } if len(hook.Workflows) == 0 { - log.Printf("[DEBUG] Not running because hook isn't connected to any workflows") - resp.WriteHeader(401) + log.Printf("[DEBUG] HOOKS: Not running because hook isn't connected to any workflows") + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflows are defined"}`))) return } if hook.Environment == "cloud" { - log.Printf("[DEBUG] This should trigger in the cloud. Duplicate action allowed onprem.") + log.Printf("[DEBUG] HOOKS: This should trigger in the cloud. Duplicate action allowed onprem.") } // Check auth @@ -1958,7 +1963,7 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { body, err := ioutil.ReadAll(request.Body) if err != nil { - log.Printf("[DEBUG] Body data error: %s", err) + log.Printf("[DEBUG] HOOKS: data read error: %s", err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return @@ -1999,7 +2004,7 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { b, err := json.Marshal(newBody) if err != nil { - log.Printf("[ERROR] Failed newBody marshaling for webhook: %s", err) + log.Printf("[ERROR] HOOKS: Failed newBody marshaling for webhook: %s", err) resp.WriteHeader(500) resp.Write([]byte(`{"success": false}`)) return @@ -2015,7 +2020,7 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { } if len(hook.Start) == 0 { - log.Printf("[WARNING] No start node for hook %s - running with workflow default.", hook.Id) + log.Printf("[ERROR] HOOKS: No start node for hook %s - running with workflow default.", hook.Id) //bodyWrapper = string(parsedBody) } @@ -2027,7 +2032,6 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { // OrgId: activeOrgs[0].Id, workflowExecution, executionResp, err := handleExecution(item, workflow, newRequest, hook.OrgId) - if err == nil { if hook.Version == "v2" { timeout := 15 @@ -2062,6 +2066,7 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { } else { resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s"}`, workflowExecution.ExecutionId))) } + return } @@ -2069,6 +2074,11 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp))) } + log.Printf("[ERROR] HOOKS: END OF FUNCTION FOR '%s'. IF this is reached, something went wrong.", hook.Id) + resp.WriteHeader(500) + resp.Write([]byte(`{"success": false, "reason": "Failed to run workflow. Check logs."}`)) + + } func handlePipelineCallback(resp http.ResponseWriter, request *http.Request) { From 063c82ee0d6444b8378519593107b2e2327830a1 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 29 May 2025 23:26:24 +0200 Subject: [PATCH 179/200] Many fixes: Notifications not being rendered even if api fetches the notifications Add copy auth id button on auth tab of admin page Added workflow link redirection for datastore key . Open docs in new tab for integration partner when it is external link. Fix multiple light theme text issue and dialog box issues. change usecase title for light theme to: --- frontend/src/components/AdminNavBar.jsx | 10 ++- frontend/src/components/AppAuthTab.jsx | 41 +++++++++++++ frontend/src/components/CacheView.jsx | 64 +++++++++++++++++++- frontend/src/components/LeftSideBar.jsx | 5 +- frontend/src/components/ParsedAction.jsx | 52 ++++++++-------- frontend/src/components/Priorities.jsx | 1 - frontend/src/components/UserManagmentTab.jsx | 30 ++++++++- frontend/src/views/Usecases2.jsx | 9 ++- frontend/src/views/Workflows2.jsx | 34 +++++++---- 9 files changed, 201 insertions(+), 45 deletions(-) diff --git a/frontend/src/components/AdminNavBar.jsx b/frontend/src/components/AdminNavBar.jsx index a37b8c15..f52e0a79 100644 --- a/frontend/src/components/AdminNavBar.jsx +++ b/frontend/src/components/AdminNavBar.jsx @@ -208,9 +208,17 @@ const AdminNavBar = (props) => { const ComponentToRender = selectedItemData.component; const componentProps = selectedItemData.props; - return ; + const updatedProps = { + ...componentProps, + notifications: notifications, + setNotifications: setNotifications, + userdata: userdata, + selectedOrganization: selectedOrganization }; + return ; +}; + const defaultImage = "/images/logos/orange_logo.svg" const imageData = selectedOrganization?.image === undefined || selectedOrganization?.image.length === 0 diff --git a/frontend/src/components/AppAuthTab.jsx b/frontend/src/components/AppAuthTab.jsx index c5b5c57f..8908590f 100644 --- a/frontend/src/components/AppAuthTab.jsx +++ b/frontend/src/components/AppAuthTab.jsx @@ -1181,6 +1181,47 @@ const AppAuthTab = memo((props) => { )} + + { + navigator.clipboard.writeText(data.id); + document.execCommand("copy"); + + toast(data.id + " copied to clipboard"); + }} + > + + + + + + + { overflowX: "auto", }}> - {["Key", "Value", "Actions", "Updated", "Distribution"].map((header, index) => ( + {["Key", "Value", "workflow", "Actions", "Updated", "Distribution"].map((header, index) => ( { backgroundColor: theme.palette.platformColor, }} > - {Array(5) + {Array(6) .fill() .map((_, colIndex) => ( { data.value } /> + + + + : ( + + + + + + + + + + ) + } + style={{ + display: "table-cell", + overflow: "hidden", + verticalAlign: "middle", + padding: "8px 8px 8px 15px", + maxWidth: 200, + overflowX: "auto", + }} + /> { - + 0 ? userdata?.active_org?.branding?.documentation_link : "/docs" } target={userdata?.active_org?.branding?.documentation_link?.length > 0 && userdata?.org_status?.includes("integration_partner") ? "_blank" : "_self" } style={hrefStyle}> { handleClose(); @@ -1633,7 +1633,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx index 7b94c67c..19bda1b8 100644 --- a/frontend/src/components/BillingStats.jsx +++ b/frontend/src/components/BillingStats.jsx @@ -33,8 +33,15 @@ import { import { BarChart, + BarSeries, + Bar, + BarLabel, + GridlineSeries, Gridline, + TooltipArea, + ChartTooltip, + TooltipTemplate, } from 'reaviz'; import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; @@ -45,20 +52,32 @@ const LineChartWrapper = ({keys, inputname, height, width}) => { const inputdata = keys.data === undefined ? keys : keys.data const {themeMode} = useContext(Context) const theme = getTheme(themeMode) - + + return (
- + {inputname} + + } + /> + } gridlines={ } /> } /> +
) } @@ -70,6 +89,7 @@ const AppStats = (defaultprops) => { const [keys, setKeys] = useState([]) const [searches, setSearches] = useState([]); const [appRuns, setAppruns] = useState(undefined); + const [childOrgsAppRuns, setChildOrgsAppRuns] = useState(undefined); const [appRunCosts, setApprunCosts] = useState(undefined); const [workflowRuns, setWorkflowRuns] = useState(undefined); const [subflowRuns, setSubflowRuns] = useState(undefined); @@ -401,6 +421,11 @@ const AppStats = (defaultprops) => { "data": [] } + var childorgappRuns = { + "key": "Child Org App Runs", + "data": [] + } + var workflowRuns = { "key": "Workflow Runs (includes subflows)", "data": [] @@ -442,6 +467,13 @@ const AppStats = (defaultprops) => { }) } + if (item["child_app_executions"] !== undefined && item["child_app_executions"] !== null) { + childorgappRuns["data"].push({ + key: new Date(item["date"]), + data: inputdata["child_app_executions"] + }) + } + // Check if workflow_executions key in item if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) { workflowRuns["data"].push({ @@ -471,6 +503,15 @@ const AppStats = (defaultprops) => { }) } + if (inputdata["daily_child_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) { + childorgappRuns["data"].push({ + key: new Date(), + data: inputdata["daily_child_app_executions"] + }) + + //setApprunCosts(appcostRuns) + } + if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) { workflowRuns["data"].push({ key: new Date(), @@ -485,6 +526,11 @@ const AppStats = (defaultprops) => { }) } + // Only for parent orgs + if (childorgappRuns["data"].length > 0) { + setChildOrgsAppRuns(childorgappRuns) + } + setSubflowRuns(subflowRuns) setWorkflowRuns(workflowRuns) setAppruns(appRuns) @@ -895,7 +941,13 @@ const AppStats = (defaultprops) => { {appRuns === undefined ? null : - + + } + + {childOrgsAppRuns === undefined ? + null + : + } {workflowRuns === undefined ? @@ -922,8 +974,9 @@ const AppStats = (defaultprops) => {
Loading usage for selected period (may take a while) + + -
: { } const newWorkflow = isEditing === true ? false : true - const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) + const priority = userdata === undefined || userdata === null || userdata.priorities === null || userdata.priorities === undefined ? null : userdata?.priorities?.find(prio => prio.type === "usecase" && prio.active === true) var upload = ""; var total_count = 0 diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 7e0ef311..d10d4f00 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -450,6 +450,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { const handleUpdateTheme = (newTheme) => { + handleThemeChange(newTheme); + setCurrentSelectedTheme(newTheme) const data = { "user_id": userdata?.id, @@ -470,14 +472,11 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }).then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - toast("Failed updating theme: ", responseJson.reason); - } else { - handleThemeChange(newTheme); - setCurrentSelectedTheme(newTheme); + toast("Failed saving your theme: ", responseJson.reason); } }) ).catch((error) => { - console.log("Error changing theme: ", error); + console.log("Error saving your theme: ", error); }); }; @@ -589,7 +588,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { } if (userdata?.org_status?.includes("integration_partner")){ handleChangeTheme(newTheme); - }else { + } else { + handleUpdateTheme(newTheme); } }} @@ -671,7 +671,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { - Version: 2.0.2 + Version: 2.1.0-rc1
diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index 0e580cbb..0267267d 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -1127,7 +1127,6 @@ const LicencePopup = (props) => { color: "white", } - console.log("Priceitem: ", shuffleVariant) // const isLoggedInHandler = () => { // if (calculatedCost === payasyougo) { // handlePayasyougo(props.userdata) @@ -1237,7 +1236,6 @@ const LicencePopup = (props) => { }); }; - console.log("Selected Organization: ", selectedOrganization.subscriptions) return (
diff --git a/frontend/src/views/Admin2.jsx b/frontend/src/views/Admin2.jsx index aaff1429..15a82580 100644 --- a/frontend/src/views/Admin2.jsx +++ b/frontend/src/views/Admin2.jsx @@ -334,7 +334,8 @@ const Admin2 = (props) => { } return ( -
+ //
+
); diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 4832777a..123f8d86 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -1349,7 +1349,9 @@ const Workflows2 = (props) => { // When there are no workflows, we can set the loading to false setIsLoadingWorkflow(false) if (currTab !== 2) { - toast("No workflows found. Showing workflow discovery") + toast.info("No workflows found in this org. Feel free to look into our public workflows!" , { + timeout: 7500, + }) setCurrTab(2) } } From cd86ce9b7457a55e3cbaf37f76979296b95eb889 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 2 Jun 2025 00:20:34 +0200 Subject: [PATCH 181/200] Changed just in case for onprem fixes to take place --- backend/app_gen/openapi/baseline/requirements.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/backend/app_gen/openapi/baseline/requirements.txt b/backend/app_gen/openapi/baseline/requirements.txt index dfad3eb9..c0d2f96f 100755 --- a/backend/app_gen/openapi/baseline/requirements.txt +++ b/backend/app_gen/openapi/baseline/requirements.txt @@ -1,3 +1,11 @@ # No extra requirements needed -requests -urllib3 +requests==2.32.3 +urllib3==2.3.0 +liquidpy==0.8.2 +MarkupSafe==3.0.2 +flask[async]==3.1.0 +python-dateutil==2.9.0.post0 +PyJWT==2.10.1 +cryptography==44.0.2 +shufflepy==0.1.0 +shuffle-sdk==0.0.25 From 75b6d60dd436b29a79c893dd0e7e0af6fd89e1a3 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 2 Jun 2025 00:30:46 +0200 Subject: [PATCH 182/200] Fixed an issue with app building onprem --- backend/go-app/docker.go | 147 +++++++++++++++++++++------------------ backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 4 +- backend/go-app/main.go | 10 +-- 4 files changed, 87 insertions(+), 76 deletions(-) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 9f0c72e2..b7ee7e6d 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -211,7 +211,7 @@ func fixTags(tags []string) []string { func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder string, downloadIfFail bool) error { ctx := context.Background() client, err := client.NewEnvClient() - defer client.Close() + defer client.Close() if err != nil { log.Printf("Unable to create docker client: %s", err) return err @@ -473,73 +473,84 @@ func buildImage(tags []string, dockerfileLocation string) error { } } } - } else { - - ctx := context.Background() - client, err := client.NewEnvClient() - defer client.Close() - if err != nil { - log.Printf("Unable to create docker client: %s", err) - return err - } - - log.Printf("[INFO] Docker Tags: %s", tags) - dockerfileSplit := strings.Split(dockerfileLocation, "/") - - // Create a buffer - buf := new(bytes.Buffer) - tw := tar.NewWriter(buf) - defer tw.Close() - baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") - - // Builds the entire folder into buf - err = getParsedTar(tw, baseDir, "") - if err != nil { - log.Printf("Tar issue: %s", err) - } - - dockerFileTarReader := bytes.NewReader(buf.Bytes()) - buildOptions := types.ImageBuildOptions{ - Remove: true, - Tags: tags, - BuildArgs: map[string]*string{}, - } - //NetworkMode: "host", - - httpProxy := os.Getenv("HTTP_PROXY") - if len(httpProxy) > 0 { - buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy - } - httpsProxy := os.Getenv("HTTPS_PROXY") - if len(httpProxy) > 0 { - buildOptions.BuildArgs["https_proxy"] = &httpsProxy - } - - // Build the actual image - imageBuildResponse, err := client.ImageBuild( - ctx, - dockerFileTarReader, - buildOptions, - ) - - if err != nil { - return err - } - - // Read the STDOUT from the build process - defer imageBuildResponse.Body.Close() - buildBuf := new(strings.Builder) - _, err = io.Copy(buildBuf, imageBuildResponse.Body) - if err != nil { - return err - } else { - if strings.Contains(buildBuf.String(), "errorDetail") { - log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) - return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) - } - } + return nil } + + ctx := context.Background() + client, err := client.NewEnvClient() + defer client.Close() + if err != nil { + log.Printf("Unable to create docker client: %s", err) + return err + } + + log.Printf("[INFO] Docker Tags: %s", tags) + dockerfileSplit := strings.Split(dockerfileLocation, "/") + + // Create a buffer + buf := new(bytes.Buffer) + tw := tar.NewWriter(buf) + defer tw.Close() + baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") + + // Builds the entire folder into buf + err = getParsedTar(tw, baseDir, "") + if err != nil { + log.Printf("[ERROR] Tar issue during app build: %s", err) + } + + dockerFileTarReader := bytes.NewReader(buf.Bytes()) + buildOptions := types.ImageBuildOptions{ + Remove: true, + Tags: tags, + BuildArgs: map[string]*string{}, + } + //NetworkMode: "host", + + httpProxy := os.Getenv("HTTP_PROXY") + if len(httpProxy) > 0 { + buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy + } + httpsProxy := os.Getenv("HTTPS_PROXY") + if len(httpProxy) > 0 { + buildOptions.BuildArgs["https_proxy"] = &httpsProxy + } + + // Print the actual file content from dockerFileTarReader + /* + data, err := ioutil.ReadAll(dockerFileTarReader) + if err != nil { + log.Printf("[ERROR] Failed reading Dockerfile TAR reader: %s", err) + } else { + log.Printf("[DEBUG] Dockerfile TAR reader content: %s", string(data)) + } + */ + + // Build the actual image + imageBuildResponse, err := client.ImageBuild( + ctx, + dockerFileTarReader, + buildOptions, + ) + + if err != nil { + return err + } + + // Read the STDOUT from the build process + defer imageBuildResponse.Body.Close() + buildBuf := new(strings.Builder) + _, err = io.Copy(buildBuf, imageBuildResponse.Body) + if err != nil { + return err + } else { + if strings.Contains(buildBuf.String(), "errorDetail") { + log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) + return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) + } + } + return nil } @@ -671,7 +682,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "No image name"}`))) return - + } log.Printf("[INFO] Trying to download image: '%s'. Appname: '%s'. BaseAppname: '%s', Split2: %s", version.Name, appname, baseAppname, appnameSplit2) @@ -870,7 +881,7 @@ func handleRemoteDownloadApp(resp http.ResponseWriter, ctx context.Context, user type tmpapp struct { Success bool `json:"success"` OpenAPI string `json:"openapi"` - App string `json:"app"` + App string `json:"app"` } app := tmpapp{} diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 449a215a..32b30400 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -22,7 +22,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.70 + github.com/shuffle/shuffle-shared v0.8.71 golang.org/x/crypto v0.37.0 google.golang.org/api v0.228.0 google.golang.org/grpc v1.71.1 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 34ccd9f6..d52c6821 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -341,8 +341,8 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.70 h1:Balbk7kIVUSgM9Lc+VJ09ZpaF18MlFxMPZdkPTD/+gU= -github.com/shuffle/shuffle-shared v0.8.70/go.mod h1:OLAwH/Ym4941Jn5DF1oZaq6iBpmjG2SNrTZ9Xqck5So= +github.com/shuffle/shuffle-shared v0.8.71 h1:OhiBpIEkn+1+uRs4nn9jPtjdT0dVfVl6VQKtaFzUtZY= +github.com/shuffle/shuffle-shared v0.8.71/go.mod h1:OLAwH/Ym4941Jn5DF1oZaq6iBpmjG2SNrTZ9Xqck5So= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 657e4a1f..c78103f5 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -35,9 +35,9 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/storage/memory" gitProxy "github.com/go-git/go-git/v5/plumbing/transport" http2 "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/go-git/go-git/v5/storage/memory" // Random xj "github.com/basgys/goxml2json" @@ -2078,7 +2078,6 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { resp.WriteHeader(500) resp.Write([]byte(`{"success": false, "reason": "Failed to run workflow. Check logs."}`)) - } func handlePipelineCallback(resp http.ResponseWriter, request *http.Request) { @@ -3095,7 +3094,7 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s return } - if user.Id == app.Owner || (user.Role == "admin" && user.ActiveOrg.Id == app.ReferenceOrg) || shuffle.ArrayContains(app.Contributors, user.Id) { + if user.Id == app.Owner || (user.Role == "admin" && user.ActiveOrg.Id == app.ReferenceOrg) || shuffle.ArrayContains(app.Contributors, user.Id) { log.Printf("[DEBUG] Editing app %s with user %s (%s) in org %s", test.Id, user.Username, user.Id, user.ActiveOrg.Id) } else { log.Printf("[WARNING] Wrong user (%s) for app %s when verifying swagger", user.Username, app.Name) @@ -3383,7 +3382,6 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s } } - log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID) if len(user.Id) > 0 { resp.WriteHeader(200) @@ -3826,6 +3824,8 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error { } } + // Send stats once every 10 times or so..? + // For now, just send every time info, err := shuffle.GetOrgStatistics(ctx, org.Id) if err != nil { log.Printf("[ERROR] Failed getting org statistics backup for org %s: %s", org.Id, err) @@ -4385,7 +4385,7 @@ func runInitEs(ctx context.Context) { } if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" { - healthcheckInterval := 60 + healthcheckInterval := 60 log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats, and dashboard on /health. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval) job := func() { // Prepare a fake http.responsewriter From 86ae5dc59da220678740f82dc09326d0329a6141 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 2 Jun 2025 18:15:01 +0200 Subject: [PATCH 183/200] Cloud sync fixes --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 115 +++++++----- frontend/src/components/Billing.jsx | 73 +++++--- frontend/src/components/BillingStats.jsx | 228 +++++++++++++---------- frontend/src/components/CloudSyncTab.jsx | 8 +- frontend/src/views/Apps2.jsx | 156 ++++++---------- frontend/src/views/Workflows2.jsx | 50 ++++- 7 files changed, 358 insertions(+), 274 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 32b30400..7f30ce38 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -22,7 +22,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.71 + github.com/shuffle/shuffle-shared v0.8.72 golang.org/x/crypto v0.37.0 google.golang.org/api v0.228.0 google.golang.org/grpc v1.71.1 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index c78103f5..70bf90ee 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -11,17 +11,18 @@ import ( "crypto/md5" "strconv" + "os" + "io" + "log" + "fmt" + "errors" + "net/url" + "os/exec" + "net/http" + "io/ioutil" + "math/rand" "encoding/hex" "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "net/url" - "os" - "os/exec" "net/http/httptest" "strings" @@ -60,7 +61,8 @@ var baseDockerName = "frikky/shuffle" var registryName = "registry.hub.docker.com" var runningEnvironment = "onprem" -var syncUrl = "https://shuffler.io" +//var syncUrl = "https://shuffler.io" +var syncUrl = "http://localhost:5002" type retStruct struct { Success bool `json:"success"` @@ -3805,32 +3807,55 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error { } } - if org.SyncConfig.WorkflowBackup { - workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser, 250, "") - if err != nil { - log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err) - } else { - backupJob.Workflows = workflows - } + shouldBackupData := false + randomNumber := rand.Intn(20) + if randomNumber == 0 { + shouldBackupData = true } - if org.SyncConfig.AppBackup && len(org.Users) > 0 { - - apps, err := shuffle.GetPrioritizedApps(ctx, foundUser) - if err != nil { - log.Printf("[ERROR] Failed getting backup apps for org %s: %s", org.Id, err) - } else { - backupJob.Apps = apps + // Check if it's 1/20 times (600 seconds - 10 min on average) + // Just to prevent it from spamming large outbound requests + if shouldBackupData { + if org.SyncConfig.WorkflowBackup { + workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser, 250, "") + if err != nil { + log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err) + } else { + backupJob.Workflows = workflows + } } - } - // Send stats once every 10 times or so..? - // For now, just send every time - info, err := shuffle.GetOrgStatistics(ctx, org.Id) - if err != nil { - log.Printf("[ERROR] Failed getting org statistics backup for org %s: %s", org.Id, err) - } else { - backupJob.Stats = *info + if org.SyncConfig.AppBackup && len(org.Users) > 0 { + foundUser.ActiveOrg.Id = org.Id + apps, err := shuffle.GetPrioritizedApps(ctx, foundUser) + if err != nil { + log.Printf("[ERROR] Failed getting backup apps for org %s: %s", org.Id, err) + } else { + parsedApps := []shuffle.WorkflowApp{} + for _, app := range apps { + if len(app.Actions) == 0 { + continue + } + + if !app.Generated { + continue + } + + parsedApps = append(parsedApps, app) + } + + backupJob.Apps = parsedApps + } + } + + // Send stats once every 10 times or so..? + // For now, just send every time + info, err := shuffle.GetOrgStatistics(ctx, org.Id) + if err != nil { + log.Printf("[ERROR] Failed getting org statistics backup for org %s: %s", org.Id, err) + } else { + backupJob.Stats = *info + } } backupJobData, err := json.Marshal(backupJob) @@ -3867,6 +3892,7 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error { //log.Printf("[ERROR] Failed cloud sync job controller run for '%s': %s", respBody, err) return err } + return nil } @@ -4004,6 +4030,8 @@ func runInitEs(ctx context.Context) { time.Sleep(30 * time.Second) } + // FIXME: This should ONLY run on one backend instance + schedules, err := shuffle.GetAllSchedules(ctx, "ALL") if err != nil { log.Printf("[WARNING] Failed getting schedules during service init: %s", err) @@ -4147,7 +4175,7 @@ func runInitEs(ctx context.Context) { } //interval := int(org.SyncConfig.Interval) - interval := 15 + interval := 30 if interval == 0 { log.Printf("[WARNING] Skipping org %s because sync isn't set (0).", org.Id) continue @@ -4249,17 +4277,17 @@ func runInitEs(ctx context.Context) { continue } - if newresp.StatusCode != 200 { - log.Printf("[WARNING] Failed stopping runs in environment %s. Status code: %d", environment, newresp.StatusCode) + + respBody, err := ioutil.ReadAll(newresp.Body) + if err != nil { + log.Printf("[ERROR] Failed setting respbody %s for execution stop. Status: %d", err, newresp.StatusCode) continue } - //respBody, err := ioutil.ReadAll(newresp.Body) - //if err != nil { - // log.Printf("[ERROR] Failed setting respbody %s", err) - // continue - //} - //log.Printf("[DEBUG] Successfully ran workflow cleanup request for %s. Body: %s", environment, string(respBody)) + if newresp.StatusCode != 200 { + log.Printf("[WARNING] Failed stopping runs in environment %s. Status code: %d. Body: %s", environment, newresp.StatusCode, string(respBody)) + continue + } url = fmt.Sprintf("http://localhost:%s/api/v1/environments/%s/rerun", backendPort, environment) req, err = http.NewRequest( @@ -4677,7 +4705,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { // If you want to disable cloud sync, see previous section. if org.CloudSync { log.Printf("[WARNING] Org %s is already syncing. Skip", org.Id) - resp.WriteHeader(401) + resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Your org is already syncing. Nothing to set up."}`))) return } @@ -4754,6 +4782,9 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { org.SyncConfig = shuffle.SyncConfig{ Apikey: responseData.SessionKey, Interval: responseData.IntervalSeconds, + + WorkflowBackup: true, + AppBackup: true, } interval := int(responseData.IntervalSeconds) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 2e86fd5c..0b9daa6f 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -2599,40 +2599,50 @@ const Billing = memo((props) => { Utilization & Stats
- {isChildOrg ? ( - - ): ( - + setCurrentTab(newValue)} + onChange={(event, newValue) => { + setCurrentTab(-1) + + // Force re-render + setTimeout(() => { + setCurrentTab(newValue) + }, 100); + }} style={{ marginTop: 20 }} TabIndicatorProps={{ style: { - height: 3, - backgroundColor: theme.palette.primary.main, - marginLeft: 12, - marginRight: 12, + height: 3, + backgroundColor: theme.palette.primary.main, + marginLeft: 12, + marginRight: 12, } }} > - - + + + {isCloud ? + + : null} + + -
+
{currentTab === 0 ?
{ userdata={userdata} />
+ : currentTab === 1 ? +
+ +
: { /> }
- - )} +
) diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx index 19bda1b8..5bb09ddb 100644 --- a/frontend/src/components/BillingStats.jsx +++ b/frontend/src/components/BillingStats.jsx @@ -84,7 +84,15 @@ const LineChartWrapper = ({keys, inputname, height, width}) => { const AppStats = (defaultprops) => { - const { globalUrl, selectedOrganization, userdata, isCloud, inputWorkflows,clickedFromOrgTab } = defaultprops; + const { + globalUrl, + selectedOrganization, + userdata, + isCloud, + inputWorkflows, + clickedFromOrgTab, + syncStats, + } = defaultprops; const [keys, setKeys] = useState([]) const [searches, setSearches] = useState([]); @@ -119,9 +127,6 @@ const AppStats = (defaultprops) => { const getWorkflowStats = async (workflow, startTime, endTime) => { - if (!userdata.support) { - return workflow - } if (workflow.id === undefined || workflow.id === null || workflow.id === "") { return workflow @@ -186,12 +191,8 @@ const AppStats = (defaultprops) => { } const loadWorkflowStats = (foundWorkflows, startTime, endTime) => { - if (!userdata.support) { - return - } - if (foundWorkflows === undefined || foundWorkflows === null || foundWorkflows.length === 0) { - console.log("Not workflows") + setResultLoading(false) return } @@ -200,6 +201,9 @@ const AppStats = (defaultprops) => { const promises = foundWorkflows.slice(0, 50).map(wf => getWorkflowStats(wf, startTime, endTime)); const allData = Promise.all(promises); + if (allData === undefined || allData === null) { + setResultLoading(false) + } allData.then((data) => { var total = 0 @@ -259,15 +263,16 @@ const AppStats = (defaultprops) => { return } - if (statistics["daily_statistics"] === undefined || statistics["daily_statistics"] === null) { + const statKey = syncStats === true ? "onprem_stats" : "daily_statistics" + if (statistics[statKey] === undefined || statistics[statKey] === null) { setFilteredStatistics(statistics) return } // Calculate month to date cost var mtd_cost = 0 - for (let key in statistics["daily_statistics"]) { - const item = statistics["daily_statistics"][key] + for (let key in statistics[statKey]) { + const item = statistics[statKey][key] if (item["date"] === undefined) { continue } @@ -325,8 +330,8 @@ const AppStats = (defaultprops) => { // Check if start time is before the daily statistics["date"] string var newlist = [] - for (let key in statistics["daily_statistics"]) { - const item = statistics["daily_statistics"][key] + for (let key in statistics[statKey]) { + const item = statistics[statKey][key] if (item["date"] === undefined) { continue } @@ -357,7 +362,7 @@ const AppStats = (defaultprops) => { var appexecutions = 0 var estimatedcost = 0 if (newlist.length > 0) { - tmpstats["daily_statistics"] = newlist + tmpstats[statKey] = newlist for (let key in newlist) { const item = newlist[key] @@ -411,7 +416,8 @@ const AppStats = (defaultprops) => { return } - const dailyStats = inputdata.daily_statistics + const statKey = syncStats === true ? "onprem_stats" : "daily_statistics" + const dailyStats = inputdata[statKey] if (dailyStats === undefined || dailyStats === null) { return } @@ -705,44 +711,57 @@ const AppStats = (defaultprops) => { style={{ textDecoration: "none", color: theme.palette.linkColor,}} >Your Organisation Statistics. It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. The billing tracker is in Beta, and is always calculated manually before being invoiced. + +
+ {syncStats !== true ? null : + "PS: You are currently looking at data from your onprem synced org"}
{filteredStatistics !== undefined ?
- - The cost of app runs in the selected period based on {filteredStatistics.monthly_app_executions} App Runs. These numbers do not exclude your included 10.000/month or {includedExecutions} App Runs per month. App Run cost: ${invocationCost}. - - }> - - - ${selectedOrganization?.lead_info?.customer === false && selectedOrganization?.lead_info?.pov === false ? - 0 - : - apprunCost - } + + {syncStats == true ? null : + + The cost of app runs in the selected period based on {filteredStatistics.monthly_app_executions} App Runs. These numbers do not exclude your included 10.000/month or {includedExecutions} App Runs per month. App Run cost: ${invocationCost}. - - Period Cost - - - + }> + + + ${selectedOrganization?.lead_info?.customer === false && selectedOrganization?.lead_info?.pov === false ? + 0 + : + apprunCost + } + + + + Period Cost + + + + } + + {syncStats === true ? null : App runs in the selected period }> - - - {filteredStatistics.monthly_app_executions === null || filteredStatistics.monthly_app_executions === undefined ? 0 : filteredStatistics.monthly_app_executions} - - - App Runs - - + + + {filteredStatistics.monthly_app_executions === null || filteredStatistics.monthly_app_executions === undefined ? 0 : filteredStatistics.monthly_app_executions} + + + App Runs + + + } + + {syncStats === true ? null : Workflow runs in the selected period @@ -757,20 +776,24 @@ const AppStats = (defaultprops) => { - - Estimated cost to be billed at the end of the current month. Subtracted contractually included app runs. Actual cost month to date: ${monthToDateCost}. App Run cost: ${invocationCost}. - - }> - - - ${monthTotalCost} + } + + {syncStats === true ? null : + + Estimated cost to be billed at the end of the current month. Subtracted contractually included app runs. Actual cost month to date: ${monthToDateCost}. App Run cost: ${invocationCost}. - - Estimated cost - - - + }> + + + ${monthTotalCost} + + + Estimated cost + + + + }
: null}
@@ -968,57 +991,58 @@ const AppStats = (defaultprops) => { */} + {syncStats === true ? null : +
+ {resultLoading ? +
+ + Loading usage for selected period (may take a while) + + + +
+ : + { + //setRowsPerPage(newPageSize) + //submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize) + }} + // event for when clicking next page + // Hide page changer + onPageChange={(params) => { + console.log("page params: ", params) + }} + onSelectionModelChange={(newSelection) => { + console.log("newSelection: ", newSelection) + //console.log("newSelection: ", newSelection) + //setSelectedWorkflowExecutionsIndexes(newSelection) + //var found = [] + //for (var i = 0; i < newSelection.length; i++) { + // // Find the workflow in the resultRows + // var selected = resultRows.find((workflow) => { + // return workflow.id === newSelection[i] + // }) -
- {resultLoading ? -
- - Loading usage for selected period (may take a while) - - - -
- : - { - //setRowsPerPage(newPageSize) - //submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize) - }} - // event for when clicking next page - // Hide page changer - onPageChange={(params) => { - console.log("page params: ", params) - }} - onSelectionModelChange={(newSelection) => { - console.log("newSelection: ", newSelection) - //console.log("newSelection: ", newSelection) - //setSelectedWorkflowExecutionsIndexes(newSelection) - //var found = [] - //for (var i = 0; i < newSelection.length; i++) { - // // Find the workflow in the resultRows - // var selected = resultRows.find((workflow) => { - // return workflow.id === newSelection[i] - // }) + // if (selected === undefined || selected === null) { + // continue + // } - // if (selected === undefined || selected === null) { - // continue - // } + // found.push(selected) + //} - // found.push(selected) - //} - - //setSelectedWorkflowExecutions(found) - }} - // Track which items are selected - /> - } -
+ //setSelectedWorkflowExecutions(found) + }} + // Track which items are selected + /> + } +
+ }
) diff --git a/frontend/src/components/CloudSyncTab.jsx b/frontend/src/components/CloudSyncTab.jsx index dd43bc4a..88df2610 100644 --- a/frontend/src/components/CloudSyncTab.jsx +++ b/frontend/src/components/CloudSyncTab.jsx @@ -483,7 +483,7 @@ const CloudSyncTab = (props) => { } else { toast("Cloud Syncronization successfully set up!"); setOrgSyncResponse( - "Successfully started syncronization. Cloud features you now have access to can be seen below." + "Successfully started syncronization. Cloud/Hybrid features are available below." ); } @@ -541,8 +541,8 @@ const CloudSyncTab = (props) => { Cloud syncronization - What does cloud sync do? Cloud synchronization is a way of getting more out of Shuffle. Shuffle will ALWAYS make every option open source, but features relying on other users can't be done without a collaborative approach. - + What does cloud sync do? Cloud synchronization is a way of getting more out of Shuffle. Shuffle will ALWAYS make every option open source, but features relying on other users can't be done without a collaborative approach. This will by default back up apps and workflows. +
{isCloud ? ( @@ -714,7 +714,7 @@ const CloudSyncTab = (props) => { )} - Features + {isCloud ? "Cloud" : "Hybrid"} Features Features and Limitations that are currently available to you in your Cloud or Hybrid Organization. App Executions (App Runs) reset monthly. If the organization is a customer or in a trial, these features limitations are not always enforced. diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index 6b062800..8c8e4284 100644 --- a/frontend/src/views/Apps2.jsx +++ b/frontend/src/views/Apps2.jsx @@ -1122,6 +1122,7 @@ const Apps2 = (props) => { const [defaultSearch, setDefaultSearch] = useState(""); const [apps, setApps] = useState([]); + const [backupApps, setBackupApps] = useState([]); const [filteredApps, setFilteredApps] = useState([]); const [appSearchLoading, setAppSearchLoading] = useState(false); const [creatorProfile, setCreatorProfile] = useState({}); @@ -1198,57 +1199,9 @@ const Apps2 = (props) => { getFramework(); }, []); - // Fetch apps based on the current tab : 0 -> org_apps, 1 -> my_apps, 2 -> all_apps - const fetchApps = async () => { - const baseUrl = globalUrl; - let url; - setIsLoading(true); - const userId = userdata?.id; - if (currTab === 1 && userId) { - url = `${baseUrl}/api/v1/users/${userId}/apps`; - } else if (currTab === 0) { - url = `${baseUrl}/api/v1/apps`; - } - try { - const response = await fetch(url, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - }); - const data = await response.json(); - if (currTab === 1) { - setAppsToShow(data); - setUserApps(data); - } else if (currTab === 0) { - setAppsToShow(data); - setOrgApps(data); - // For testing the empty state - // setAppsToShow([]); - // setOrgApps([]); - } - setIsLoading(false); - } catch (err) { - console.error("Error fetching apps:", err); - setIsLoading(false); - } - }; - useEffect(() => { - - // Only fetch if we have required data - if (globalUrl && (currTab === 0 || (currTab === 1 && userdata?.id))) { - fetchApps(); - } - }, [currTab, globalUrl, userdata?.id]); // Remove location.search dependency - - // useEffect(() => { - // // setSearchQuery(""); - // setSelectedCategory([]); - // setSelectedLabel([]); - // }, [currTab]) - + getApps() + }, []) // Find top categories and tags based on the current tab useEffect(() => { @@ -1293,11 +1246,13 @@ const Apps2 = (props) => { }); }; + /* useEffect(() => { if (serverside) { return null; } }, [serverside]); + */ const getApps = () => { // Get apps from localstorage @@ -1308,7 +1263,7 @@ const Apps2 = (props) => { if (storageApps === null || storageApps === undefined || storageApps.length === 0) { storageApps = [] } else { - setAppsToShow(storageApps) + //setAppsToShow(storageApps) setOrgApps(storageApps) setApps(storageApps) // setFilteredApps(storageApps) @@ -1344,18 +1299,25 @@ const Apps2 = (props) => { var privateapps = []; var valid = []; var invalid = []; + + var backups = [] for (var key in responseJson) { const app = responseJson[key]; - if (app.categories !== undefined && app.categories !== null && app?.categories.includes("Eradication")) { + if (app?.reference_info?.onprem_backup === true) { + backups.push(app) + continue + } + + if (app?.categories !== undefined && app?.categories !== null && app?.categories?.includes("Eradication")) { app.categories = ["EDR"] } - if (app.is_valid && !(!app.activated && app.generated)) { + if (app?.is_valid && !(!app?.activated && app?.generated)) { privateapps.push(app); } else if ( - app.private_id !== undefined && - app.private_id.length > 0 + app?.private_id !== undefined && + app?.private_id.length > 0 ) { valid.push(app); } else { @@ -1363,6 +1325,11 @@ const Apps2 = (props) => { } } + console.log("BACKUPAPPS: ", backups) + if (backups.length > 0) { + setBackupApps(backups) + } + privateapps.push(...valid); privateapps.push(...invalid); console.log("privateapps: setting apps ", privateapps) @@ -1372,39 +1339,22 @@ const Apps2 = (props) => { // setFilteredApps(privateapps); if (privateapps.length > 0) { - if (selectedApp.id === undefined || selectedApp.id === null) { - if (privateapps[0].owner !== undefined && privateapps[0].owner !== null) { - getUserProfile(privateapps[0].owner); + if (selectedApp?.id === undefined || selectedApp?.id === null) { + if (privateapps[0]?.owner !== undefined && privateapps[0]?.owner !== null) { + getUserProfile(privateapps[0]?.owner); } - - // setContact(privateapps[0].contact_info) - - // setSelectedApp(privateapps[0]); - // setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you") } - - // if ( - // privateapps[0].actions !== null && - // privateapps[0].actions.length > 0 - // ) { - // setSelectedAction(privateapps[0].actions[0]); - // } else { - // setSelectedAction({}); - // } } - if (privateapps.length > 0 && storageApps.length === 0) { + if (privateapps?.length > 0 && storageApps?.length === 0) { try { localStorage.setItem("apps", JSON.stringify(privateapps)) } catch (e) { console.log("Failed to set apps in localstorage: ", e) } } - - //setTimeout(() => { - // setFirstLoad(false) - //}, 5000) }) .catch((error) => { + console.log("Failed to get apps: ", error.toString()); toast(error.toString()); setIsLoading(false); }); @@ -1780,7 +1730,6 @@ const Apps2 = (props) => { // setOpenModal(true); }; - useEffect(() => { const apps = currTab === 1 ? userApps : orgApps; const filteredUserAppdata = filterApps(apps, searchQuery, selectedCategory, selectedLabel); @@ -1798,33 +1747,38 @@ const Apps2 = (props) => { } else if (newTab === 1) { const filteredUserApps = filterApps(userApps, searchQuery, selectedCategory, selectedLabel); setAppsToShow(filteredUserApps); - } + } else if (newTab === 3) { + const filteredUserApps = filterApps(backupApps, searchQuery, selectedCategory, selectedLabel); + setAppsToShow(filteredUserApps); + return + } // Update URL query params based on tab index const tabMapping = { 0: 'org_apps', 1: 'my_apps', - 2: 'all_apps' + 2: 'all_apps', + 3: 'backup_apps', }; - const queryParams = new URLSearchParams(location.search); - queryParams.set('tab', tabMapping[newTab]); + const queryParams = new URLSearchParams(location.search); + queryParams.set('tab', tabMapping[newTab]); - // Maintain search query in URL regardless of tab - if (searchQuery) { - queryParams.set('q', searchQuery); - } else { - queryParams.delete('q'); - } + // Maintain search query in URL regardless of tab + if (searchQuery) { + queryParams.set('q', searchQuery); + } else { + queryParams.delete('q'); + } - navigate(`${location.pathname}?${queryParams.toString()}`); + navigate(`${location.pathname}?${queryParams.toString()}`); }; // Update useEffect for filtering without URL manipulation useEffect(() => { if (currTab === 2) return; // Skip for "Discover Apps" tab as it uses Algolia - const apps = currTab === 1 ? userApps : orgApps; + const apps = currTab === 1 ? userApps : currTab === 3 ? backupApps : orgApps; const filteredApps = filterApps(apps, searchQuery, selectedCategory, selectedLabel); setAppsToShow(filteredApps); }, [searchQuery, selectedCategory, selectedLabel, currTab, userApps, orgApps]); @@ -1914,7 +1868,7 @@ const Apps2 = (props) => {
- {currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Apps + {currTab === 0 ? "Org" : currTab === 1 ? "Your" : currTab === 3 ? "Backup" : "Discover"} Apps {isCloud ? null : ( @@ -2027,6 +1981,7 @@ const Apps2 = (props) => { ...(currTab === 1 ? tabActive : {}) }} /> + { ...(currTab === 2 ? tabActive : {}) }} /> + + {backupApps.length > 0 && + + }
@@ -2043,7 +2009,7 @@ const Apps2 = (props) => { minWidth: "25%", maxWidth: "25%" }}> - {(currTab === 0 || currTab === 1) ? ( + {(currTab === 0 || currTab === 1 || currTab === 3) ? ( {
{ - currTab === 0 && ( + currTab === 0 || currTab === 3 && (
{isLoading ? ( @@ -2285,7 +2251,7 @@ const Apps2 = (props) => { handleAppClick={handleAppClick} leftSideBarOpenByClick={leftSideBarOpenByClick} userdata={userdata} - fetchApps={fetchApps} + fetchApps={getApps} setUserApps={setUserApps} appsToShow={appsToShow} @@ -2338,7 +2304,7 @@ const Apps2 = (props) => { {appsToShow.map((data, index) => ( { var upload = ""; const [workflows, setWorkflows] = React.useState([]); + const [backupWorkflows, setBackupWorkflows] = React.useState([]); const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove const [selectedUsecases, setSelectedUsecases] = React.useState([]); const [filteredWorkflows, setFilteredWorkflows] = React.useState([]); @@ -756,7 +757,8 @@ const Workflows2 = (props) => { const tabMapping = { 0: 'org_workflows', 1: 'my_workflows', - 2: 'all_workflows' + 2: 'all_workflows', + 3: 'backup_apps', }; const queryParams = new URLSearchParams(location.search); queryParams.set('tab', tabMapping[newValue]); @@ -1357,15 +1359,25 @@ const Workflows2 = (props) => { } var newarray = [] + var backupWf = [] for (var wfkey in responseJson) { const wf = responseJson[wfkey] if (wf.public === true || wf.hidden === true) { continue } + if (wf?.backup_config?.onprem_backup === true) { + backupWf.push(wf) + continue + } + newarray.push(wf) } + if (backupWf.length > 0) { + setBackupWorkflows(backupWf) + } + var setProdFilter = false var actionnamelist = []; @@ -4332,6 +4344,17 @@ const Workflows2 = (props) => { }} /> + {backupWorkflows.length > 0 && + + } + { @@ -4341,7 +4364,7 @@ const Workflows2 = (props) => { ...tabStyle, marginRight: 0, marginLeft: 25, - ...(currTab === 3 ? tabActive : {}) + ...(currTab === 4 ? tabActive : {}) }} /> @@ -4676,8 +4699,6 @@ const Workflows2 = (props) => { paddingBottom: 40 }}> - - {currTab === 0 && orgWorkflows.map((data, index) => { // Shouldn't be a part of this list if (data.public === true) { @@ -4699,6 +4720,27 @@ const Workflows2 = (props) => { ) })} + {currTab === 3 && backupWorkflows.map((data, index) => { + // Shouldn't be a part of this list + if (data.public === true) { + return null + } + + // if (firstLoad) { + // workflowDelay += 75 + // } else { + // return + // } + + return ( + + {/**/} + + {/**/} + + ) + })} + { currTab === 1 && myWorkflows.map((data, index) => { if (data.public === true) { From 4a867d9f1b5a61581f89c4e30b5d8a97bfaf7ebf Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 2 Jun 2025 22:45:07 +0200 Subject: [PATCH 184/200] Repush with proper sync url --- backend/go-app/go.sum | 4 ++-- backend/go-app/main.go | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index d52c6821..154bcdde 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -341,8 +341,8 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.71 h1:OhiBpIEkn+1+uRs4nn9jPtjdT0dVfVl6VQKtaFzUtZY= -github.com/shuffle/shuffle-shared v0.8.71/go.mod h1:OLAwH/Ym4941Jn5DF1oZaq6iBpmjG2SNrTZ9Xqck5So= +github.com/shuffle/shuffle-shared v0.8.72 h1:HVOsRt83/1k9P+8q1FAxXnDKyROoDAFa1A3MnoRJYb0= +github.com/shuffle/shuffle-shared v0.8.72/go.mod h1:OLAwH/Ym4941Jn5DF1oZaq6iBpmjG2SNrTZ9Xqck5So= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 70bf90ee..eaab40da 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -61,8 +61,8 @@ var baseDockerName = "frikky/shuffle" var registryName = "registry.hub.docker.com" var runningEnvironment = "onprem" -//var syncUrl = "https://shuffler.io" -var syncUrl = "http://localhost:5002" +var syncUrl = "https://shuffler.io" +//var syncUrl = "http://localhost:5002" type retStruct struct { Success bool `json:"success"` @@ -3807,13 +3807,14 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error { } } + // Check if it's 1/20 times (600 seconds - 10 min on average) + // Only problem: May take time to sync the first time, which is annoying shouldBackupData := false randomNumber := rand.Intn(20) if randomNumber == 0 { shouldBackupData = true } - // Check if it's 1/20 times (600 seconds - 10 min on average) // Just to prevent it from spamming large outbound requests if shouldBackupData { if org.SyncConfig.WorkflowBackup { @@ -4285,7 +4286,10 @@ func runInitEs(ctx context.Context) { } if newresp.StatusCode != 200 { - log.Printf("[WARNING] Failed stopping runs in environment %s. Status code: %d. Body: %s", environment, newresp.StatusCode, string(respBody)) + if !strings.Contains(string(respBody), "is active") { + log.Printf("[WARNING] Failed stopping runs in environment %s. Status code: %d. Body: %s", environment, newresp.StatusCode, string(respBody)) + } + continue } From cdffb159f9b18e28485f98fd7b8185141de50210 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Tue, 3 Jun 2025 14:09:28 +0530 Subject: [PATCH 185/200] if not login then show dark theme --- frontend/src/App.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 7f110c98..13289352 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -92,7 +92,7 @@ const App = (message, props) => { const [dataset, setDataset] = useState(false) const [isLoaded, setIsLoaded] = useState(false) const [curpath, setCurpath] = useState(typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname) - const { themeMode, handleThemeChange, setBrandColor, brandColor} = useContext(Context); + const { themeMode, handleThemeChange, setBrandColor, brandColor,setThemeMode} = useContext(Context); const currentTheme = getTheme(themeMode, brandColor); const mainColor = currentTheme?.palette?.backgroundColor const [isPreviousThemeLight, setIsPreviousThemeLight] = useState(false) @@ -197,7 +197,11 @@ const App = (message, props) => { }else{ handleThemeChange("dark") } - } + }else { + handleThemeChange("dark") + setThemeMode("dark") + localStorage.removeItem("theme"); + } // Handling Ethereum update From 90d909e5da8a1d8186a8f3a048ac52ffa2d87061 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 3 Jun 2025 11:43:05 +0200 Subject: [PATCH 186/200] Fix of the app page --- backend/go-app/main.go | 1 + frontend/src/views/Apps2.jsx | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index eaab40da..4495d7aa 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3809,6 +3809,7 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error { // Check if it's 1/20 times (600 seconds - 10 min on average) // Only problem: May take time to sync the first time, which is annoying + // This is to ensure that we don't spam the shuffle cloud servers with a lot of data shouldBackupData := false randomNumber := rand.Intn(20) if randomNumber == 0 { diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index 8c8e4284..da7dd5c8 100644 --- a/frontend/src/views/Apps2.jsx +++ b/frontend/src/views/Apps2.jsx @@ -347,7 +347,6 @@ const Hits = ({ } }, [currTab, window.location]); - //Function for activation and deactivation of app const handleActivateButton = (event, data, type) => { @@ -1205,7 +1204,7 @@ const Apps2 = (props) => { // Find top categories and tags based on the current tab useEffect(() => { - if (currTab === 0 || currTab === 1) { + if (currTab === 0 || currTab === 1 || currTab === 3) { setCategories(findTopCategories()); setLabels(findTopTags()); } @@ -1325,19 +1324,21 @@ const Apps2 = (props) => { } } - console.log("BACKUPAPPS: ", backups) if (backups.length > 0) { setBackupApps(backups) } privateapps.push(...valid); privateapps.push(...invalid); - console.log("privateapps: setting apps ", privateapps) + setAppsToShow(privateapps); setOrgApps(privateapps); setApps(privateapps); - // setFilteredApps(privateapps); + //setFilteredApps(privateapps); + const filteredOrgApps = filterApps(privateapps, searchQuery, selectedCategory, selectedLabel); + setAppsToShow(filteredOrgApps) + if (privateapps.length > 0) { if (selectedApp?.id === undefined || selectedApp?.id === null) { if (privateapps[0]?.owner !== undefined && privateapps[0]?.owner !== null) { @@ -1345,6 +1346,7 @@ const Apps2 = (props) => { } } } + if (privateapps?.length > 0 && storageApps?.length === 0) { try { localStorage.setItem("apps", JSON.stringify(privateapps)) @@ -2106,6 +2108,7 @@ const Apps2 = (props) => { ))} + {selectedCategory.length > 0 && ( {
{ - currTab === 0 || currTab === 3 && ( + currTab !== 0 && currTab !== 3 ? + null + : + (
{isLoading ? ( From 6ad756f8d8497661ec6f57f165c384111625fa28 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 4 Jun 2025 18:37:36 +0200 Subject: [PATCH 187/200] Sync from cloud for 2.1.0-rc1 --- frontend/src/components/ApiExplorer.jsx | 2 +- frontend/src/components/AppAuthTab.jsx | 161 ++++++++++++++++++- frontend/src/components/Branding.jsx | 13 +- frontend/src/components/CacheView.jsx | 11 +- frontend/src/components/EditWorkflow.jsx | 12 +- frontend/src/components/LeftSideBar.jsx | 16 +- frontend/src/components/ParsedAction.jsx | 83 +++++++--- frontend/src/components/UserManagmentTab.jsx | 12 +- frontend/src/components/ssoTab.jsx | 62 ++++++- frontend/src/views/Admin2.jsx | 4 +- frontend/src/views/AppCreator.jsx | 41 ++++- frontend/src/views/AppExplorer.jsx | 24 +-- frontend/src/views/UpdateAuthentication.jsx | 2 + 13 files changed, 357 insertions(+), 86 deletions(-) diff --git a/frontend/src/components/ApiExplorer.jsx b/frontend/src/components/ApiExplorer.jsx index 45e6eab5..d1321220 100644 --- a/frontend/src/components/ApiExplorer.jsx +++ b/frontend/src/components/ApiExplorer.jsx @@ -1463,7 +1463,7 @@ const ActionsList = memo(({
0 ? openapi?.info["x-logo"] : theme?.palette?.defaultImage} width={48} height={48} alt="app logo" diff --git a/frontend/src/components/AppAuthTab.jsx b/frontend/src/components/AppAuthTab.jsx index 8908590f..8c65366e 100644 --- a/frontend/src/components/AppAuthTab.jsx +++ b/frontend/src/components/AppAuthTab.jsx @@ -83,18 +83,17 @@ const AppAuthTab = memo((props) => { const [appAuthenticationGroupId, setAppAuthenticationGroupId] = React.useState(""); const [appAuthenticationGroups, setAppAuthenticationGroups] = React.useState([]); const [appAuthenticationGroupName, setAppAuthenticationGroupName] = React.useState(""); + const [selectedSubOrg, setSelectedSubOrg] = useState([]); const [appAuthenticationGroupDescription, setAppAuthenticationGroupDescription] = React.useState(""); const [appsForAppAuthGroup, setAppsForAppAuthGroup] = React.useState([]); const [searchQuery, setSearchQuery] = React.useState(""); const [showAppModal, setShowAppModal] = useState(false) + const [selectedAuthId, setSelectedAuthId] = useState(""); + const [showDistributionPopup, setShowDistributionPopup] = useState(false); const [showAuthenticationLoader, setShowAuthenticationLoader] = useState(true) const [showAppAuthGroupLoader, setShowAppAuthGroupLoader] = useState(true) const { themeMode, supportEmail, brandColor } = useContext(Context) const theme = getTheme(themeMode, brandColor) - const changeDistribution = (data) => { - //changeDistributed(data, !isDistributed) - editAuthenticationConfig(data.id, "suborg_distribute") - } useEffect(() => { getAppAuthentication(); @@ -214,11 +213,39 @@ const AppAuthTab = memo((props) => { }); }; - const editAuthenticationConfig = (id, parentAction) => { + const handleSelectSubOrg = (id, action) => { + if (action === "all") { + const childOrgs = userdata.orgs.filter( + (data) => data.creator_org === userdata.active_org.id + ); + setSelectedSubOrg((prev) => { + if (prev.length === childOrgs.length) { + // If all child orgs are already selected, clear the selection + return []; + } else { + // Otherwise, select all child org IDs + return childOrgs.map((data) => data.id); + } + }); + } else if (action === "none") { + setSelectedSubOrg([]); + } else { + setSelectedSubOrg((prev) => { + if (prev.includes(id)) { + return prev.filter((data) => data !== id); + } else { + return [...prev, id]; + } + }); + } + }; + + const editAuthenticationConfig = (id, parentAction, selectedSuborgs) => { const data = { id: id, action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere", - } + selected_suborgs: selectedSuborgs !== undefined && selectedSuborgs !== null ? selectedSuborgs : [], + } const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config"; @@ -240,6 +267,7 @@ const AppAuthTab = memo((props) => { } else { toast("Successfully updated auth!"); setSelectedUserModalOpen(false); + setShowDistributionPopup(false); setTimeout(() => { getAppAuthentication(); }, 1000); @@ -251,6 +279,106 @@ const AppAuthTab = memo((props) => { }); }; + + const changeDistribution = (id, selectedSubOrg) => { + + editAuthenticationConfig(id, "suborg_distribute", [...new Set(selectedSubOrg)]) + } + + + const cacheDistributionModal = showDistributionPopup ? ( + {setShowDistributionPopup(false);setSelectedAuthId("")}} + PaperProps={{ + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + fontFamily: theme?.typography?.fontFamily, + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + zIndex: 1000, + minWidth: "600px", + minHeight: "320px", + overflow: "auto", + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogActions-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + }, + }} + > + + + Select sub-org to distribute Datastore key + + + + {handleSelectSubOrg(null, "none")}}>None + {handleSelectSubOrg(null, "all")}}>All + {userdata.orgs.map((data, index) => { + if (data.creator_org !== userdata.active_org.id) { + return null; + } + + const imagesize = 22; + const imageStyle = { + width: imagesize, + height: imagesize, + pointerEvents: "none", + marginRight: 10, + marginLeft: data.id === userdata.active_org.id ? 0 : 20, + }; + + const image = data.image === "" ? ( + {data.name} + ) : ( + {data.name} + ); + + return ( + handleSelectSubOrg(data.id)} + style={{ display: "flex", alignItems: "center" }} + > + + {image} + {data.name} + + ); + })} + +
+ + +
+
+
+ ) : null; + const editAuthenticationModal = selectedAuthenticationModalOpen ? ( { return (
{appModal} + {cacheDistributionModal}
@@ -1004,7 +1133,7 @@ const AppAuthTab = memo((props) => { ]; } - const isDistributed = data.suborg_distributed === true ? true : false; + const isDistributed = data?.suborg_distribution?.length > 0 || data?.suborg_distributed ? true : false; var validIcon = if (data.validation !== null && data.validation !== undefined && data.validation.valid === false) { @@ -1260,7 +1389,23 @@ const AppAuthTab = memo((props) => { style={{ }} color="secondary" onClick={() => { - changeDistribution(data, !isDistributed) + setShowDistributionPopup(true) + if(data?.suborg_distribution?.length > 0){ + setSelectedSubOrg(data.suborg_distribution) + }else{ + setSelectedSubOrg([]) + } + setSelectedAuthId(data.id) + if (data?.suborg_distributed) { + const allSuborg = userdata?.orgs?.map((data, index) => { + if (data.creator_org !== userdata.active_org.id) { + return null; + } + return data.id; + }) + setSelectedSubOrg(allSuborg.filter((data) => data !== null)) + } + }} /> diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index 40a76846..f913a58a 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -33,7 +33,7 @@ const Branding = (props) => { //const alert = useAlert(); const [publishingInfo, setPublishingInfo] = useState(""); const [publishRequirements, setPublishRequirements] = useState([]) - const [currentSelectedTheme, setCurrentSelectedTheme] = useState("dark"); + const [currentSelectedTheme, setCurrentSelectedTheme] = useState(themeMode); const [integrationPartner, setIntegrationPartner] = useState(false); const [changingTheme, setChangingTheme] = useState(false); const theme = getTheme(themeMode, brandColor) @@ -190,6 +190,13 @@ const Branding = (props) => { }) }; + useEffect(() => { + if (userdata && userdata?.active_org && userdata?.active_org?.branding?.theme?.length > 0) { + console.log("Setting current selected theme from userdata", userdata?.active_org?.branding?.theme); + setCurrentSelectedTheme(userdata?.active_org?.branding?.theme); + } + },[userdata]); + // Should enable / disable org branding const handleChangePublishing = () => { console.log("Handle change publishing"); @@ -247,10 +254,6 @@ const Branding = (props) => { setCurrentSelectedTheme(userdata?.active_org?.branding.theme); } - if (userdata.support) { - setCurrentSelectedTheme(localStorage.getItem("theme") || "dark"); - } - if ( selectedOrganization && selectedOrganization?.branding?.brand_color && diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 9c45df62..8502c81f 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -213,9 +213,16 @@ const CacheView = memo((props) => { const cache = { key: dataValue.key, value: value, - category: selectedCategory, + category: selectedCategory, } + if (listCache.length > 0) { + const selectedCache = listCache.find((data) => data.key === dataValue.key); + if (selectedCache?.suborg_distribution?.length > 0) { + cache.suborg_distribution = selectedCache.suborg_distribution; + } + } + setCacheInput([cache]); fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, { @@ -805,7 +812,7 @@ const CacheView = memo((props) => { }} > - Category (beta) + Category } diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index a156e4d6..dbd8d3ea 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -212,13 +212,7 @@ const EditWorkflow = (props) => { //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, borderRadius: theme.palette.DialogStyle.borderRadius, - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - '& .MuiDialogContent-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - '& .MuiDialogTitle-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, + backgroundColor: themeMode === "dark" ? "black" : theme?.palette?.DialogStyle?.backgroundColor, }, }} > @@ -299,10 +293,10 @@ const EditWorkflow = (props) => { right: 20, bottom: 0, zIndex: 1002, - backgroundColor: theme.palette.DialogStyle.backgroundColor, height: 75, paddingTop: 20, paddingLeft: 30, + backgroundColor: themeMode === "dark" ? "#262626" : theme.palette.DialogStyle.backgroundColor, }}>
+ {userdata && userdata?.active_org?.creator_org?.length > 0 ? null : +
+ + Skip SSO for Admins. When enabled, parent org admins will be able switch to the sub-organization without SSO. + +
+ +
+
+ }
{ defaults, sso_config, lead_info, - { mfa_required } = {} + { mfa_required } = {}, + editing, ) => { const data = { name: name, @@ -248,6 +249,7 @@ const Admin2 = (props) => { sso_config: sso_config, lead_info: lead_info, mfa_required: mfa_required !== undefined ? mfa_required : selectedOrganization?.mfa_required, + editing: editing?.length > 0 ? editing : "", }; const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index ad7aaf48..ee6d8831 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1973,7 +1973,7 @@ const AppCreator = (defaultprops) => { // Saving the app that's been configured. // Save SAVE app const submitApp = () => { - toast("Uploading and building app " + name); + toast.info("Uploading and building app " + name + ". This may take a minute or two."); setAppBuilding(true); setErrorCode(""); @@ -2600,15 +2600,23 @@ const AppCreator = (defaultprops) => { } if (setExtraAuth.length > 0) { + const invalidfieldnames = ["apikey", "username", "password", "username_basic", "password_basic", "url", "access_token"] for (let authkey in extraAuth) { const curauth = extraAuth[authkey]; - if (curauth.name.length === 0 || curauth.name.toLowerCase() == "url") { toast("Can't add extra auth with empty name or Name URL"); setAppBuilding(false); return; } + // Additional comparisonchecks + if (authenticationOption !== "No authentication" && authenticationOption !== "") { + if (invalidfieldnames.includes(curauth.name.toLowerCase())) { + toast.warn("Skipping extra auth field: " + curauth.name + ". This is not valid.") + continue + } + } + data.components.securitySchemes[curauth.name] = { type: "apiKey", in: curauth.type, @@ -2662,9 +2670,15 @@ const AppCreator = (defaultprops) => { if (responseJson.reason !== undefined) { setErrorCode(responseJson.reason); - toast.error("Failed to build: " + responseJson.reason, { - autoClose: 10000 - }) + if (responseJson.details !== undefined && responseJson.details !== null) { + toast.error("Failed to build - contact support@shuffler.io: " + responseJson.details, { + autoClose: 60000 + }) + } else { + toast.error("Failed to build: " + responseJson.reason, { + autoClose: 10000 + }) + } } } else { toast.success("Successfully built openapi app! Added job to rebuild it in your hybrid runtime locations (Orborus)."); @@ -2678,7 +2692,7 @@ const AppCreator = (defaultprops) => { .catch((error) => { setAppBuilding(false); setErrorCode(error.toString()); - toast(error.toString()); + toast.error(error.toString()); }); }; @@ -2810,10 +2824,21 @@ const AppCreator = (defaultprops) => { margin="normal" variant="outlined" defaultValue={extraAuth[index].name} + helperText={ + + {extraAuth[index]?.name?.toLowerCase() === "url" + || extraAuth[index]?.name?.toLowerCase() === "apikey" + ? `ERROR: Invalid key: ${extraAuth[index].name}. ` : ""} + + } onChange={(e) => { extraAuth[index].name = e.target.value; setExtraAuth(extraAuth); }} + onBlur={(e) => { + // Forcerender + setUpdate(Math.random()); + }} InputProps={{ classes: { notchedOutline: classes.notchedOutline, @@ -5865,7 +5890,9 @@ const AppCreator = (defaultprops) => {

- {name}{" "} + 0 ? `/apps/${props?.match?.params?.appid}` : "/apps" } style={{ textDecoration: "none", color: theme.palette.text.primary }}> + {name}{" "} + {actions === null || actions === undefined || actions.length === 0 ? null : ( diff --git a/frontend/src/views/AppExplorer.jsx b/frontend/src/views/AppExplorer.jsx index 94bd3ed3..63c81d63 100644 --- a/frontend/src/views/AppExplorer.jsx +++ b/frontend/src/views/AppExplorer.jsx @@ -3121,12 +3121,12 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"; style={{ marginBottom: 0, marginLeft: 0, marginRight: 0, minWidth: 800, maxWidth: 800, margin: "auto", }} aria-label="disabled tabs example" > - } label="Docs" /> - : } label={appType === 0 || appType === 2 ? "Explore the API" : "Try it out"} /> + } label="Docs" /> + : } label={appType === 0 || appType === 2 ? "Try the API" : "Try it out"} /> - } style={{color: theme.palette.text.primary}} label="Stats" /> - } disabled style={{color: theme.palette.text.secondary}} label="Integrations" /> - } disabled={userdata.support !== true} style={{color: userdata.support !== true ? theme.palette.text.secondary: theme.palette.text.primary}} label="Creator" value={4} /> + } style={{color: theme.palette.text.primary, textTransform: "none", }} label="Stats & Downloads" /> + } style={{color: theme.palette.text.primary, textTransform: "none", }} disabled style={{color: theme.palette.text.secondary}} label="Integrations" /> + } disabled={userdata.support !== true} style={{color: userdata.support !== true ? theme.palette.text.secondary: theme.palette.text.primary, textTransform: "none", }} label="Creator" value={4} />
{selectedTab === 1 && app.skipped_build == false ? ( @@ -3135,8 +3135,8 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";
- {currentAction.description !== undefined && currentAction.description !== null && currentAction.description !== "" ? -
- Due to using docker containers with privately uploaded containers, we had to use a custom registry. Use the command below to download the image to the server if it fails to run. + Custom uses a custom Docker registry for private containers. Use the command below to download the app image to a server if it fails to do so automatically. - It will authenticate and authorize you, before redirecting to a Signed URL on https://storage.googleapis.com + The downloadable image may not be ready until 5 minutes after the app was built. -  Now also works for ARM containers! +  Now also works for ARM containers (?arch=arm)!
) : null} - {selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.id !== undefined && userdata.support === true ? + {selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.id !== undefined ? // Iconbutton for authentication with just an icon. Link to /apps/authentication?app_id=app.id - Explore API + Try the API + Add Key + + + + + + {datastoreCategories !== undefined && + datastoreCategories !== null && + datastoreCategories.length > 1 ? ( + + + + Category + + + + ) : null} + +
+ {renderTextBox ? + + + + : + + + + } + + {renderTextBox && { + handleKeyDown(event); + if(event.key === 'Enter' && selectedFileId.length > 0){ + setUpdateToThisCategory(event.target.value) + } + + }} + style={{ + height: 35, + width: 200, + marginTop: 0, + }} + InputProps={{ + style: { color: theme.palette.textFieldStyle.color, - minWidth: 122, - maxWidth: 122, height: 35, - float: "right", - position: 'relative', - top: 8 - }} - value={selectedCategory} - onChange={(event) => { - //if (selectAllChecked || listCache.length > 0) { - if (selectAllChecked || selectedFiles.length > 0) { - setUpdateToThisCategory(event.target.value) - setShowFileCategoryPopup(true) - return - } + fontSize: 16, + borderRadius: 4, + paddingTop: 0, + }, + }} + color="primary" + placeholder="Category name" + required + margin="dense" + defaultValue={""} + autoFocus + />} +
+
- setSelectedCategory(event.target.value) - if (event.target.value === "all" || event.target.value === "default") { - listOrgCache(orgId) - } else { - listOrgCache(orgId, event.target.value) - } - - // Add it to the url as a query - if (window.location.search.includes("category=")) { - const newurl = window.location.href.replace(/category=[^&]+/, `category=${event.target.value}`) - window.history.pushState({ path: newurl }, "", newurl) - } else { - window.history.pushState({ path: window.location.href }, "", `${window.location.href}&category=${event.target.value}`) - } - }} - > - {fileCategories.map((data, index) => { - return ( - - {data.replaceAll("_", " ")} - - ); - })} - - { - setShowFileCategoryPopup(false) - }} - > - File Categories - - Please note that your selected files ({selectedFileId?.length}) will be moved to the {updateToThisCategory} category. - - - - - - - - ) : null} + + + + + + + + - - : - - - } + - {renderTextBox && { - handleKeyDown(event); - if(event.key === 'Enter' && selectedFileId.length > 0){ - //setShowFileCategoryPopup(true) - setUpdateToThisCategory(event.target.value) - } + {showAutomationMenu || showSettingsMenu ? + { + setShowAutomationMenu(false) + setShowSettingsMenu(false) + }} + PaperProps={{ + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: 500, + minHeight: 700, + fontFamily: theme?.typography?.fontFamily, + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + zIndex: 1000, + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogActions-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + }, + }} + > + + + + {showSettingsMenu === true ? +
+ + + Settings for category '{selectedCategory}' + + +
+
+ + Timeout + + + You can set a timeout for the category. This will delete all keys in this category after the specified time. Timeout is in seconds and based on last EDITED time. + +
+ { + // Check if it's a number or not + var timeoutValue = 0 + if (isNaN(e.target.value) || e.target.value === "" || e.target.value === null) { + toast.info("Timeout must be a number. Setting to 0.") + } else { + timeoutValue = parseInt(e.target.value, 10) + if (timeoutValue < 60) { + toast.info("Timeout must be between 60 seconds or more. Setting to 0.") + } + + if (timeoutValue === categoryConfig?.settings?.timeout) { + return + } + } + + setCategorySettingsField("timeout", timeoutValue) + }} + /> +
+ +
+
+ + {categoryConfig?.settings?.public === true ? "" : "NOT"} Public + + + This will make the url for this category public. Metadata will be cleared, except for timestamps. Types: keys,ndjson,csv,values,json,meta + +
+
URL (when public):
{globalUrl}/api/v2/datastore/category/{selectedCategory}?top=10000&type=keys&org_id={orgId} +
+
+
+
+ + { + setCategorySettingsField("public", e.target.checked) + setUpdate(Math.random()) // Force update to re-render the component + }} + style={{marginTop: 10, }} + color="secondary" + /> + +
+
+ +
+
+ + Subscribing + + + Enabling this feature will allow other organizations to subscribe to this category. This is NOT fully available yet. + +
+
+
+ : +
+ + + Automation for category '{selectedCategory}' + + + + When + + + A key is edited + + + + Do + + {categoryAutomations.map((automation, index) => { + + return ( + + ) + })} +
+ } - }} - style={{ - height: 35, - width: 200, - marginTop: 0, - }} - InputProps={{ - style: { - color: theme.palette.textFieldStyle.color, - height: 35, - fontSize: 16, - borderRadius: 4, - paddingTop: 0, - }, - }} - color="primary" - placeholder="Category name" - required - margin="dense" - defaultValue={""} - autoFocus - />}
+ + + : null} +
+ {isSelectedDataStore? null :} -
- - - {["Key", "Value", "workflow", "Actions", "Updated", "Distribution"].map((header, index) => ( - - ))} - - {cachedLoaded === false - ? [...Array(6)].map((_, rowIndex) => ( - - {Array(6) - .fill() - .map((_, colIndex) => ( - - - - ))} - - )) - : listCache?.length === 0 ? ( - - {Array(5).fill().map((_, index) => ( - - ))} - - ): listCache?.map((data, index) => { - var category = selectedCategory - if (selectedCategory === "default") { - category = "" + { + setSelectedRows(newSelection) + }} + keepNonExistentRowsSelected={false} + getRowId={(row) => row.key} + + autoHeight={true} + sx={{ + marginTop: 1, + height: listCache.length*52+500, + width: "100%", + '.MuiTablePagination-selectLabel, .MuiTablePagination-select, .MuiTablePagination-selectIcon': { + display: 'none', + }, + marginBottom: 20, + }} + + loading={cachedLoaded === false} + pagination + paginationMode="server" + page={page} + rowCount={totalAmount} + onPageChange={(newPage, second) => { + listOrgCache(orgId, selectedCategory, 0, pageSize, newPage) + + setPage(newPage) + }} + onPageSizeChange={(newSize) => { + setPageSize(newSize); + setPage(0) + setSelectedRows([]) + + setCursors({ + 0: "", + }) + }} + + + filterMode="client" + onFilterModelChange={(model) => { + // Specific search for the key itself to find it fast across the index + if (model?.items?.length === 1) { + if (model?.items[0]?.operatorValue === "equals" && model?.items[0]?.columnField === "key") { + // Run backend search for a specific key + listOrgCache(orgId, selectedCategory, 0, pageSize, page, model?.items[0]?.value) + } + } + }} + + getRowHeight={() => { + return "auto" + }} + + hideFooterSelectedRowCount={true} + hideFooter={true} + /> + +
+
+ + {page * pageSize + 1} - {Math.min((page + 1) * pageSize, totalAmount)} of {totalAmount} + + + { + var disabled = false + if (item?.type === "page") { + if (cursors[item.page-1] === undefined) { + disabled = true + } + } + if (item?.type === "previous") { + disabled = page === 0 + } + + if (cachedLoaded === false) { + disabled = true + } + + return ( + + ) + + }} + onChange={(e, value) => { + if (value < 1) { + return } - if (data?.category === undefined && category === "") { - } else if (data?.category !== category) { - return null - } + const newPage = value-1 - var bgColor = isSelectedDataStore? themeMode === "dark" ? "#212121" : "#FFFFFF" :"#27292d"; - if (index % 2 === 0) { - bgColor = isSelectedDataStore? themeMode === "dark" ? "#1A1A1A" : "#EAEAEA" :"#1f2023"; - } + listOrgCache(orgId, selectedCategory, 0, pageSize, newPage) - const validate = validateJson(data.value); - const isDistributed = data?.suborg_distribution?.length > 0 ? true : false; - return ( - - - { - // handleReactJsonClipboard(copy); - }} - collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} - iconStyle={theme.palette.jsonIconStyle} - displayDataTypes={false} - onSelect={(select) => { - // HandleJsonCopy(showResult, select, data.action.label); - console.log("SELECTED!: ", select); - }} - name={"value"} - /> - : - data.value + setPage(newPage) + }} + /> + + {selectedRows.length > 0 ? +
+ + deleteEntry(orgId, selectedRows[key], foundCategory, false) + } + + setSelectedRows([]) + setTimeout(() => { + // Refresh the list + listOrgCache(orgId, selectedCategory, 0, pageSize, page) + toast.success("Deleted " + selectedRows.length + " keys from datastore") + }, 2500) + + }} + variant={"outlined"} + color="secondary" + > + + Delete {selectedRows.length} Key{ selectedRows.length > 1 ? "s" : "" } + + : null} +
+
+

); -}); +}) +//export default CacheView; export default memo(CacheView); diff --git a/frontend/src/components/CollectIngestModal.jsx b/frontend/src/components/CollectIngestModal.jsx new file mode 100644 index 00000000..afba4afa --- /dev/null +++ b/frontend/src/components/CollectIngestModal.jsx @@ -0,0 +1,205 @@ +import React, { useState, useEffect, useContext, memo } from "react"; + +import { Context } from "../context/ContextApi.jsx"; +import { getTheme } from "../theme.jsx"; +import { GetIconInfo } from "../views/Workflows2.jsx"; +import { toast } from 'react-toastify'; + +import { + Dialog, + DialogTitle, + DialogContent, + Typography, + Paper, + LinearProgress, + Grid, + Button, +} from '@mui/material'; + +import { + Rocket as RocketIcon, + FilterAlt as FilterAltIcon, +} from '@mui/icons-material'; + +const CollectIngestModal = (props) => { + const { globalUrl, open, setOpen } = props; + + const { themeMode, brandColor } = useContext(Context); + const theme = getTheme(themeMode, brandColor); + + if (open === undefined || open === null) { + console.error("CollectIngestModal: 'open' prop is required."); + return null + } + + if (setOpen === undefined || setOpen === null) { + console.error("CollectIngestModal: 'setOpen' prop is required."); + return null + } + + const startIngestion = (appname, index) => { + console.log("APPNAME:", appname, "INDEX:", index) + + const body = { + "app_name": appname, + "label": appname, + } + + const url = `${globalUrl}/api/v2/workflows/generate` + fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + credentials: "include", + }) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) + .then((data) => { + console.log("Ingestion started successfully:", data); + toast.success(`Ingestion for ${appname} started successfully!`); + }) + .catch((error) => { + console.error("Error starting ingestion:", error); + toast.error(`Failed to start ingestion for ${appname}. Please try again.`); + }); + + } + + const IngestItem = (props) => { + const { type, index } = props + + const [hovering, setHovering] = useState(false); + const [isFinished, setIsFinished] = useState(false); + + const appname = type + const ingestedAmount = 20 + + const iconDetails = GetIconInfo({ + "app_name": appname, + "name": appname, + }) + + return ( + // setHovering(true)} + onMouseLeave={() => setHovering(false)} + > +
+ {iconDetails?.originalIcon && ( + iconDetails?.originalIcon + )} + + + + {appname} + +
+ + + {hovering ? +
+
+ : null} + + {isFinished ? +
+ + {ingestedAmount} / X + + +
+ : null} +
+ ) + } + + return ( + { + setOpen(false) + }} + > + + + + + + + Collection and Ingestion + + + + + + + + + + + + + + ) +} + +export default CollectIngestModal diff --git a/frontend/src/components/Countries.jsx b/frontend/src/components/Countries.jsx index 2cb9c853..590e4caf 100644 --- a/frontend/src/components/Countries.jsx +++ b/frontend/src/components/Countries.jsx @@ -142,7 +142,6 @@ const countries = [ suggested: true, }, { code: 'GA', label: 'Gabon', phone: '241' }, - { code: 'GB', label: 'United Kingdom', phone: '44' }, { code: 'GD', label: 'Grenada', phone: '1-473' }, { code: 'GE', label: 'Georgia', phone: '995' }, { code: 'GF', label: 'French Guiana', phone: '594' }, @@ -178,7 +177,6 @@ const countries = [ { code: 'IE', label: 'Ireland', phone: '353' }, { code: 'IL', label: 'Israel', phone: '972' }, { code: 'IM', label: 'Isle of Man', phone: '44' }, - { code: 'IN', label: 'India', phone: '91' }, { code: 'IO', label: 'British Indian Ocean Territory', @@ -390,12 +388,6 @@ const countries = [ }, { code: 'UA', label: 'Ukraine', phone: '380' }, { code: 'UG', label: 'Uganda', phone: '256' }, - { - code: 'US', - label: 'United States', - phone: '1', - suggested: true, - }, { code: 'UY', label: 'Uruguay', phone: '598' }, { code: 'UZ', label: 'Uzbekistan', phone: '998' }, { diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 27ecf529..eeae20bc 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -681,7 +681,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { - Version: 2.1.0-rc1 + Version: 2.1.0-rc2 @@ -745,6 +745,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { localStorage.setItem("globalUrl", ""); localStorage.setItem("getting_started_sidebar", "open"); + localStorage.removeItem("workflows"); + localStorage.removeItem("apps"); fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { mode: "cors", @@ -913,7 +915,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { "US": "us", "EU": "eu", "CA": "ca", - "UK": "gb" + "UK": "gb", + "au": "au", }; region = regionMapping[region_url] || "eu"; @@ -1465,7 +1468,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { { + + + + ): null} diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 4bf7ad17..07be2cb8 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -61,9 +61,9 @@ const menuData = { { title: "Singul", description: - "Connect and run actions seamlessly between different platforms.", + "Connect your favorite services with a singul line of code.", icon: "/images/logos/singul.svg", - path: "https://singul-docs.gitbook.io/singul/getting-started", + path: "https://singul.io", gaData: { category: "navbar", action: "products_click", @@ -662,9 +662,9 @@ const MobileMenu = ({ anchorEl, handleClose, isLoggedIn, navigate, isCloud }) => action: "partners_click", label: "go_to_partners", }) - handleItemClick('/partners') + handleItemClick('/become-partner') }else{ - window.open("https://shuffler.io/partners", '_blank'); + window.open("https://shuffler.io/become-partner", '_blank'); return; } }} @@ -1391,26 +1391,151 @@ const Navbar = (props) => { > Pricing - + + + + + + + + ); diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 1e090762..fe7b5649 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -4,7 +4,7 @@ import { makeStyles, createStyles } from "@mui/styles"; import {getTheme} from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; -import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; +import { validateJson, GetIconInfo } from "../views/Workflows2.jsx"; import { GetParsedPaths } from "../views/Apps.jsx"; import { sortByKey } from "../views/AngularWorkflow.jsx"; import { NestedMenuItem } from "mui-nested-menu"; @@ -825,7 +825,7 @@ const ParsedAction = (props) => { // Check if array, and if first item is object + success if (Array.isArray(valid.result) && valid.result.length > 0 && typeof valid.result[0] === "object") { if (valid.result[0].success !== false) { - exampleData = valid.result[0] + exampleData = valid.result break } } else { @@ -910,14 +910,48 @@ const ParsedAction = (props) => { if (newActionList?.length > 0) { let appParentActions = parentActionList?.map(action => "$" + action.name.toLowerCase()); let notPresentAction = actions?.filter((action) => !appParentActions?.includes(action)) + + // Extract all variable references from paramvalue + // Examples of what it matches: + // - Simple variables: $test, $myVar, $x + // - Variables with underscores: $my_variable, $ok_ok, $testing_nice, $nice_try_man + // - Workflow data access patterns: $not_customer_copy.body.subject.#, $not_customer.body.subject.text + // - Deep nested properties: $email_action.response.items[0].sender.address + + const variablePattern = /\$[a-zA-Z0-9_]+(?=\.|,|;|:|\s|"|'|`|\)|\]|\}|$)/g; + const foundVariables = paramvalue.match(variablePattern) || []; + notPresentAction?.forEach((action) => { action = action.replace(" ", "_"); - if (paramvalue.includes(action)) { + + // Check if the exact action is in the foundVariables list + if (foundVariables.includes(action)) { errorVars.push(action); - // paramvalue = paramvalue.replace(action, "") - // paramvalue = paramvalue.replace(/^\s*[\r\n]/gm, ""); } - }) + }); + + // Check for variables that don't exist in the workflow at all + foundVariables.forEach(variable => { + // Skip checking for $ followed by system vars as exec and shuffle_cache. + if (variable.match(/\$(exec|shuffle_cache)/)) { + return; + } + // Create list of all valid action variables from newActionList + const newActionNames = newActionList?.map(action => "$" + action.name.toLowerCase()) || []; + + // Extract workflow and execution variables from newActionList + const workflowVars = newActionList + .filter(item => item.type === "workflow_variable" || item.type === "execution_variable") + .map(item => "$" + item.autocomplete.toLowerCase()); + + // If the variable doesn't exist in any workflow actions, parent actions, newActionList, or extracted variables, show error + if (!actions.includes(variable) && + !appParentActions.includes(variable) && + !newActionNames.includes(variable) && + !workflowVars.includes(variable.toLowerCase())) { + errorVars.push(variable); + } + }); } } @@ -3085,9 +3119,6 @@ const ParsedAction = (props) => { } var isAppSelected = false - - console.log("App found: ", app.name, app.categories, appIndex, newactionname) - const paramIndex = selectedAction.parameters.findIndex((param) => param.name === "app_name") if (paramIndex > -1) { // Check the actual value and if it's the same diff --git a/frontend/src/components/PartnerApps.jsx b/frontend/src/components/PartnerApps.jsx new file mode 100644 index 00000000..60e6e1b5 --- /dev/null +++ b/frontend/src/components/PartnerApps.jsx @@ -0,0 +1,21 @@ +import { Box, Typography } from '@mui/material' +import React from 'react' + +const PartnerApps = () => { + return ( + + Partner Apps + + ) +} + +export default PartnerApps diff --git a/frontend/src/components/PartnerDetails.jsx b/frontend/src/components/PartnerDetails.jsx new file mode 100644 index 00000000..755e187d --- /dev/null +++ b/frontend/src/components/PartnerDetails.jsx @@ -0,0 +1,987 @@ +import React, { memo, useEffect, useState, useContext } from "react"; + +import { makeStyles } from "@mui/styles"; +import { toast } from "react-toastify"; +import { getTheme } from "../theme.jsx"; +import { Context } from "../context/ContextApi.jsx"; +import countries from "./Countries.jsx"; + +import { + FormControl, + InputLabel, + Paper, + OutlinedInput, + Checkbox, + Card, + Tooltip, + FormControlLabel, + Chip, + Link, + Typography, + Switch, + Select, + MenuItem, + Divider, + ListItemText, + TextField, + Button, + Tabs, + Tab, + Grid, + Autocomplete, + Skeleton, + Box, +} from "@mui/material"; +const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#f85a3e !important", + }, +}); + +const PartnerDetails = (props) => { + const { + userdata, + selectedOrganization, + setSelectedOrganization, + globalUrl, + isCloud, + adminTab, + isEditOrgTab, + partnerData, + loadingPartnerData, + setPartnerData, + } = props; + + const classes = useStyles(); + const defaultBranch = "main"; + const ITEM_HEIGHT = 48; + const ITEM_PADDING_TOP = 8; + const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 400, + borderRadius: 10, + overflowY: "scroll", + }, + }, + getContentAnchorEl: () => null, + }; + + const expertiseOptions = [ + "Cybersecurity", + "Cloud Security", + "Managed Detection and Response (MDR)", + "Incident Response", + "Penetration Testing", + "Compliance and Governance", + "Network Security", + "Endpoint Security", + "Identity and Access Management (IAM)", + "SIEM", + ]; + const servicesOptions = [ + "Cybersecurity Consulting", + "Managed Security", + "Incident Response", + "Penetration Testing", + "Cloud Security", + "Managed Detection", + "Endpoint Security", + "Network Security", + "Identity Management", + "Compliance", + "Security Training", + "Security Management", + "Threat Intelligence", + "Web Security", + "Database Security", + "Disaster Recovery", + "IT Security Audit", + "Cybersecurity Training", + "Security Automation", + "AI Security", + "IoT Security", + "Cloud Security Broker", + "Security Service", + "Managed Security Provider", + "Cybersecurity Service", + ] + + const solutionsOptions = [ + "EDR", + "MDR", + "XDR", + "Case Management", + "SIEM", + "Vulnerability Management", + "IPS", + "IDS", + "Threat Intelligence", + "IAM", + "Data Security", + "Incident Response", + "Cloud Security", + "Network Security", + ] + + const { themeMode } = useContext(Context); + const theme = getTheme(themeMode); + const [isDisabled, setIsDisabled] = useState(isCloud ? userdata?.active_org?.is_partner ? false : true : true); + const handleExpertiseChange = (event) => { + const { value } = event.target; + setPartnerData({ + ...partnerData, + expertise: value, + }); + }; + + useEffect(() => { + setIsDisabled(isCloud ? userdata?.active_org?.is_partner ? false : true : true); + if(userdata?.support) { + setIsDisabled(false); + } + }, [userdata, isCloud]); + + const handleServicesChange = (event) => { + const { value } = event.target; + setPartnerData({ + ...partnerData, + services: Array.isArray(value) ? value : [], + }); + }; + + const handleSolutionsChange = (event) => { + const { value } = event.target; + setPartnerData({ + ...partnerData, + solutions: Array.isArray(value) ? value : [], + }); + }; + + const setSelectedRegion = (region) => { + // send a POST request to /api/v1/orgs/{org_id}/region with the region as the body + const regionMap = { + US: "us-west2", + EU: "europe-west2", + CA: "northamerica-northeast1", + UK: "europe-west2", + "EU-2": "europe-west3", + AUS: "australia-southeast1", + }; + + region = regionMap[region] || region; + + var data = { + dst_region: region, + }; + + toast.info( + "Changing region to " + region + "...This may take a few minutes." + ); + + fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/change/region`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(data), + timeOut: 1000, + }).then((response) => { + if (response.status !== 200) { + response + .json() + .then((reason) => { + toast.error("Failed to change region: " + reason.reason); + }) + .catch((err) => { + toast.error("Failed to change region"); + }); + } else { + toast("Region changed successfully! Reloading in 5 seconds.."); + // Reload the page in 2 seconds + setTimeout(() => { + window.location.reload(); + }, 5000); + } + + // return responseJson + }); + }; + + if (loadingPartnerData) { + return ( +
+ + + +
+
+
+
+
+ + Name + + +
+ {/*
+
+ Expertise +
+ +
+
+
+ Services +
+ +
*/} +
+
+ Solutions +
+ +
+
+ + Region + + +
+
+
+ Country +
+ +
+
+
+ + Description + + +
+
+
+ + Website URL + + +
+
+ + Article URL + + +
+
+
+
+ + + +
+ ); + } + + return ( +
+ + + +
+
+
+
+
+ + Name + + {}} + onChange={(e) => { + if (e.target.value.length > 100) { + toast("Choose a shorter name."); + return; + } + + setPartnerData({ + ...partnerData, + name: e.target.value, + }); + }} + color="primary" + InputProps={{ + style: { + color: theme.palette.textFieldStyle.color, + height: "35px", + fontSize: "1em", + borderRadius: 4, + backgroundColor: + theme.palette.textFieldStyle.backgroundColor, + }, + classes: { + notchedOutline: isEditOrgTab + ? null + : classes.notchedOutline, + }, + }} + /> +
+ {/*
+
+ Expertise +
+ + + +
+
+
+ Services +
+ + + +
*/} +
+
+ Solutions +
+ + + +
+
+ + Region + + +
+
+ + Country + + + country.label === partnerData?.country)} + style={{ + minWidth: 210, + marginTop: 5, + maxWidth: 210, + height: 35, + borderRadius: 4, + color: theme.palette.textFieldStyle.color, + cursor: isDisabled ? "not-allowed" : "pointer", + }} + disabled={isDisabled} + options={countries} + autoHighlight + onClear={() => { + setPartnerData({ + ...partnerData, + country: "", + }); + }} + getOptionLabel={(option) => option?.label || ""} + onChange={(event, newValue) => { + setPartnerData({ + ...partnerData, + country: newValue?.label, + }); + }} + renderOption={(props, option) => ( + img": { mr: 2, flexShrink: 0 } }} + {...props} + > + + {option.label} + + )} + renderInput={(params) => ( + country.label === partnerData.country)?.code.toLowerCase()}.png`} + alt="" + style={{ marginRight: 8 }} + /> + ) : null, + style: { + color: theme.palette.textFieldStyle.color, + height: "35px", + fontSize: "1em", + borderRadius: 4, + backgroundColor: theme.palette.textFieldStyle.backgroundColor, + }, + classes: { + notchedOutline: isEditOrgTab ? null : classes.notchedOutline, + }, + }} + /> + )} + /> + +
+
+
+ + Description + +
+ { + setPartnerData({ + ...partnerData, + description: e.target.value, + }); + }} + onChange={(e) => { + setPartnerData({ + ...partnerData, + description: e.target.value, + }); + }} + InputProps={{ + classes: { + notchedOutline: isEditOrgTab + ? null + : classes.notchedOutline, + }, + style: { + color: theme.palette.textFieldStyle.color, + height: 89, + borderRadius: 4, + }, + }} + /> +
+
+
+ + Website URL + + {}} + onChange={(e) => { + if (e.target.value.length > 100) { + toast("Choose a shorter website URL."); + return; + } + + setPartnerData({ + ...partnerData, + website_url: e.target.value, + }); + }} + color="primary" + InputProps={{ + style: { + color: theme.palette.textFieldStyle.color, + height: "35px", + fontSize: "1em", + borderRadius: 4, + backgroundColor: + theme.palette.textFieldStyle.backgroundColor, + }, + classes: { + notchedOutline: isEditOrgTab + ? null + : classes.notchedOutline, + }, + }} + /> +
+
+ + Article URL + + {}} + onChange={(e) => { + if (e.target.value.length > 100) { + toast("Choose a shorter article URL."); + return; + } + + setPartnerData({ + ...partnerData, + article_url: e.target.value, + }); + }} + color="primary" + InputProps={{ + style: { + color: theme.palette.textFieldStyle.color, + height: "35px", + fontSize: "1em", + borderRadius: 4, + backgroundColor: + theme.palette.textFieldStyle.backgroundColor, + }, + classes: { + notchedOutline: isEditOrgTab + ? null + : classes.notchedOutline, + }, + }} + /> +
+
+
+
+ + + +
+ ); +}; + +export default PartnerDetails; + +const RegionChangeModal = memo( + ({ + isDisabled, + selectedOrganization, + setSelectedRegion, + userdata, + partnerData, + setPartnerData, + }) => { + // Show from options: "us-west2", "europe-west2", "europe-west3", "northamerica-northeast1" + // var regions = ["us-west2", "europe-west2", "europe-west3", "northamerica-northeast1"] + + const regionMapping = { + "Africa": "af", + "Asia Pacific (APAC)": "", + "Europe": "eu", + "Latin America": "la", + "Middle East": "me", + "North America": "na", + "Global": "" + }; + + //let regiontag = "UK"; + let regiontag = partnerData?.region || "UK"; + let regionCode = "gb"; + + const regionsplit = selectedOrganization?.region_url?.split("."); + + if (regionsplit?.length > 2 && !regionsplit[0]?.includes("shuffler")) { + const namesplit = regionsplit[0]?.split("/"); + regiontag = namesplit[namesplit.length - 1]; + + if (regiontag === "california") { + regiontag = "US"; + regionCode = "us"; + } else if (regiontag === "frankfurt") { + regiontag = "EU-2"; + regionCode = "eu"; + } else if (regiontag === "ca") { + regiontag = "CA"; + regionCode = "ca"; + } else if (regiontag === "austrailia") { + regiontag = "AUS"; + regionCode = "au"; + } + } + + return ( + + {/* Region */} + + + ); + } +); diff --git a/frontend/src/components/PartnerHeader.jsx b/frontend/src/components/PartnerHeader.jsx new file mode 100644 index 00000000..8cb84c0a --- /dev/null +++ b/frontend/src/components/PartnerHeader.jsx @@ -0,0 +1,845 @@ +import React, { memo, useEffect, useState, useContext } from "react"; +import {getTheme} from "../theme.jsx"; +import { makeStyles } from "@mui/styles"; +import { toast } from 'react-toastify'; +import { Context } from "../context/ContextApi.jsx"; +import { + Tooltip, + TextField, + FormControl, + InputLabel, + OutlinedInput, + Checkbox, + Select, + MenuItem, + Button, + ListItemText, + Dialog, + DialogActions, + DialogContent, + Divider, + DialogTitle, + Skeleton, +} from "@mui/material"; + +import AvatarEditor from "react-avatar-editor"; + + +import { + AddAPhotoOutlined as AddAPhotoOutlinedIcon, + ZoomInOutlined as ZoomInOutlinedIcon, + ZoomOutOutlined as ZoomOutOutlinedIcon, + Loop as LoopIcon, + AddPhotoAlternate as AddPhotoAlternateIcon, +} from "@mui/icons-material"; + +import { + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, + Save as SaveIcon, +} from "@mui/icons-material"; + +const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#f85a3e !important", + }, +}); + +const defaultImage = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAK4AAACuCAYAAACvDDbuAAAgAElEQVR4Xu19e9CvV1Xe3r/vnJOcBEhBSgMEBaoUK9POCOVmAuP0HwcUCNYZSUsh9xv3hGl1qNippQRE20IFEhQoBJiaKVpEgQRnhD+0QHSmRkAsxE4doBZQTs71u/zezruv6/Ksvffvkn/qd8bBfN/3XvZe+1nPevbaa+/XuxX/Tbe6C/ece8qOd5dNk3um9+7Jk/OPds49zE3uyPy4ST5zCr/y4f+sfxO4j16vHsqfmV7gpgm8Yzm/lP9+km1B9+W2zn/zzk2sDeR55ffy3enn1Lf5J/1e3bb42kX43/Do1nt9fUdpLrSbbFt8vvls+idlm/qsaJPWuNK/TfvOLU44577pnPuSc9PvT95/9szu3p9c/IH/c2oVKDbeyB8z/Yz7noNd9zzn3U+5hX+6m9wjnXM7GqXCHpbR0+PnjudGxEtBkzRo02vFtRB83rkAXPqPGD4MnmWGGa3CDqp9+pp4Bwd2dCwPzIXur6D1xaGRXaQz8nf4Kfix1/3joDXtDm0jHVbYspirbZdkj4Npcv/XO/8F59xdfv/o7zz0A1/9yxEAd4E7/by74OCke5Fb+Bvd5J7unDvGHmyCKiMzX40ByW+XQCxvyoSafgGMBT0fgTayXmA/kykr8PI1kS0JIOMPRiSJ7ctOWfuI+mcw4Uj7lO25Y0TQoyHGbKsjJbqXR5HoKijK6nuz3bPhokEX+f4956Z7nXP/8cz+w//bxR/4H00GbgJ3ep170sHC/WxgWecvUJ5AemrLA4NF2cCAa4BDSCbjYVR2RYN2ZXlAOgxZqcXUDLh9toyvSkybopAJ+KBbOpHADOEjoO1EkVYU6LB0HbPUDtaN8N7TzrmPOnfwry9631/8mcW+JnD3Xu+eu3DubZNzT1Xhu8eyxaXw47lWBIzcAW2fGSpoM1WPgzYNWqSRwqjsnSYokgMyvW5JnzWZNrQpC9tC+ZVZW1Ek9GbBQK8ZuS99mrrW1sPh7XXsTeDmqHrvYvKve+j77/8MAi9E1v6t7vnOu7c7555gaM4SuiHGOsajcV8xmeEUMSDFf4yJQLiUmpYNTg906iVCl7b6ltu4gi5lTMs6Z4RpwLQsVMP+IZaNTtYngRoNI1uOzwdYhJxtEhqKAFsdvoI0XHy/m3ZeedH7v/ZxCV7Vir1b3HP8wr3PBC1HDsog5IkJxSdDHAyBlRqBgyHwAANuI3sg3j4G+shSHASQaRlbZtDW8AmiT7b3TLJD8qACrWrwkclYg2l70qACWow5IZvQ9lHQUmeZ7p+8v/Lh7/3z36NDw6w7a9rljvvw5NwPWykOOuEono5YT8IvXQNBK5yhGnwFVoCgZYPYZgvSBtnGMMkJnR2ZrIh3Fga3mGoR9YgpL2xAjbGlBC2YTEGWJu01J3mkrwYGuFP25EF1XMFj9y6Wiyse9p+/9hXl2il78G7n3T9radomMVphpMdGUG9wAJRLYJ4Wz2CZ73QmUpkxa9jlA2fmKvNz0414Fm+Abw6b+U8D4Km+xfsb0l5E99Z+S4mApIHhaEz2bCAPSmMkaNMzW4Av94ac3vtPHdu96TG3f32evFXhuH+ru8J5f4dzrmYPVGiKN7Bfsx/syVj+C07AA3WQmlYBa4TR+bpWnhYCgjyLtN+OBrbezPdAwNcQCjq4EAsaCEAS8BF4PNIZoBKhWdo9PMO0DX5vbmGTRMi41SgyxrQlTcmsVQB+2jt/3UXv/9qdxVLTre5RBwv/m25yz+Qspe2NQWt5ZEQ5x79kMgzaoTC4DM9SuoqDvcEWSqKsNlnJHYvvQ5oWvRutiInrijdwALGBLZ003sEcMl7DSEdHoGTHyoQ4eqTxaqymzdmYuq40wrS1D5A80i+985/1u0de/LAPf+Vb4Y7917uXO+dud84fRcClIOKdVwPGQZRuNBuj6DujMHakCUCgadUsubeMS1A/vhRLBxZEgR6g8juLbUzAE1tWe8wslv9hh9HyQPknZNpEAsUQaJWPDgpttyCjsDxuTMQMwAOiUoTknNv1frrmovfd/wE/1x4cTP4jbuF+3AItwFe81ACGXm0SAC/5UTYGoSBAdwCwmZE9GAqhInmvBr8JPMmAo0zLB7G5YmdMALkTy/fOz5+NonO0VaJl0Bmyh3jDakzL2xL6JnLF8dGjk8xKDBWPtc3euY+dWT7kJX73VveMhfe/4Zy7ONOcgDpnP+a+iC1WLpZJ7RPhArLCPD7snZSVpnmGkgMZTdIrA5BIwOoHBkDLHWs1eRBMZ/Wr2FWGTcFmGQSMZYhjAInAB1C2WQIlrBGUi1JtUaJ8DHrtVIJtwyjZ6bxKjA3Q1n590y8Wl/uDW9wt08LfFgpmCCgx81FroU40KqhUvNLPYu+0NBSYiBUckjDHxtX0eCJJeqBN7cfh2WazKa/FK4MK+wGmHV/t40wbmyoBj8erYDQ5VGYC1VzFmBZpjUzEjGIjhREO5NS2A+f9v/T7t7q7nPc/qZPbYuhZeJdGL6lIMgFQ12gsJUBxwObLxP1KHujO+8nQZT15ANiOsrRcooZVXjDEyyovAzypbDKNQRifdSZiFXTI9m2JQCNBZcAcv+x7w9iF/zEACwlLZEd6ZZiFmWI7vJvu8gevd/dNk/+hPFDa02rwtXOZdZij8axlUoldwnilAYaRDKa1c5ixizxMVqfAEcUIo122RW2OCwv8/aPSotolLn5Y+nBkRWxk4aQuqGu7GOMxE5nPtQcN0DLggvGGYySjRR3L1L77/P6t/tvOuUc0gdvSZb0qL+hxtbSw/tkGma49SB3J2sl4R0SOBqNm+AYbFW9Hz0KgqIPIgWsCgHhzvaZ9rwYsjgKyzZiJMVmNVoiNsi3oWxi3BlkUwshMW7JO35mBu+dc3LmgwNvUfZVNasfH5AHVX/kOvHNBDzbXfX3jZkxnThliwSwtSswcLQDng7haFVUEWb99umAm6+5iyzA0yDbcntDmhQSwo2U5EvPzI3pWOzzDC/OaLtMm0Pm9GbhVpkD0hheX9hZwpxdWPYQ6iiqMweCgUMizB7nBYtXOeKeo0optXC1Mg8WFagMzAtV62mpKwCiC4miBdcCNSRg2aOnQNSu4Amn5MAi2LGyzoF2aaDtLflfCS2kDtBPRtBVf+cr4Dr+XgMs7Tn9CA86uNkDBr4k/AY0jQ0XRsoIdUqV9NXjLUXrywAJ8Yj2LBYrXovsrqMwI1Mhlhn6xUWq/I9tzTKsT1hNO15dNkqV5VFHAE7bTzpGep/6Afi8jXXWMBnBB2CeGXVUeaNAaYV5kD3IYLP5mFpTwlFSkx9VraWv1m+UYNqB46AL2Q6yf2lgr0HRoDX1PBTk1EliEgkO8dPhcLs+Ba9ybTRsuXmVFLI6Cwgoihh7gBcFx4JohKoli8nAJqGhczbIZQIWsynXCSA2mHQ6Don0YuHYY5HvEWsArMKs6by2Wthml9pkzeWwVilwI8KQPrdJEU/rI+xtsa/SfQwKQ1RRlKL1OLwpph6rAtUEbH5z+Xkm3LyFoOMtXwwkLBO1g8XgeSADa7COlzWqASHhKS0RU0rAwqJwyLrOGCUrD4XFKrtqusDRMefEVsfyaVR0ya17AK7GLrWXmsoxGioOKYXB4p0SjCA7ZyqpFNh3KTRG4LbZIf4vMKXa66hYKyh2ZJfNl3Poe6hhW9iCxDGkjBF6nAJwHi5EQT1hwrrBYYatOjkDM5I3FCxrJxmt9OaDs2oP2ZCqMd2jo6osL7SiJbcww1okCfu+WzNK5mXUjXjZafmAc4Lb2ZSzF1IOlafnzqIcaOw9qc0jnKhD6LMByu8Tbx3KhYiAt0ALDZ9uRqUJiAhm9UPagioTS15ZDkrqIEu0U0WBNW5+fbwDABflXjhMiXSxihDZK76x/I4+t7U3ARR1AtbRCR5mxR4TC1myarYhx7RaWcNkoU8t3KrXsMGOsqInoEJqCnA2F7x5Lz88m9m9FOMpu5LqxsksepfBuaquuAsiXYG5QT6vG3SCf1nVCMpSgGTqKHYqN/t4tCxN+sAjcvjopDvnSRiJcLOOW9fkm6LAnjw0sdryxe9Hyqnhetmyr5hSDNqGarLqxsxWkYxhEkydtyX6mz7P2aaerTVwNtENRT/Rfw6mzcJJu8BC4Bf55JEjnGsDV9bRGI1TdQZUgVVdZUUCyeSsKGJ7bOvfALD6viwsVEONOysOvBTxZTxtTekWDC5aSsixcR5weArcxEVt1qw1lxnVAW/ycSRhgU3Xhwmng1haE7BCuV2XSO/0AKn4QyMWKWKQaOYlrhArmVEL3mQOrwR0v7YX4PBlNxwT1nm+ExiwS2gfEVdCOti0afgTYluyR0my9ZVwbtNl17D1szPk6EsSF1FmYg00KuP0TCTVo1SY3GOo7RyL1QFFop2afMmzDrTgEF6eqbGSE93CBdphcT8tlUx/wehuR9V5UBL7i3rfEtPhQPQu08ffBqQoZrFZ7wHGWbGKNAxnfdeUB1dsMuCZoFTC0hGiHQnz4XGaMuN1jLOVVo8YIeMigRT/lk6Tig3bZIHcKW3pwdxZyxnTK0doDBHoJFM6erD1W9kFJwnFNC8HHcMLbB6NIa9LO5EP+obavAheE4HIvCvkpTKk/WUaqupY4ee6cAkSKrqjzOuzTkIkA1HRIw3iFaUHRi9K4lkRoRhEN2jjuKzpkM7cenpWVChnOFKuK1h/L08J0ngU+I11GMWUeu8pAi5eY/d7rYlahT/scEklbsf1JJNSmx3lv1dLyk1usMBqZsFpesEoFhhqcov1aEgJKmrktqQi8KT9Im0UYDMAeBC1tuLFVR/SNs/mcMsQBsV/WCEoTK2GAvvdDPHI6MfGuIVMcR4XvtRY/AHDJAyDTijBYrCZZU8oDYGATOCTE03BOHawJjDpoOERZuUyUORgJ0/Wa0qyGU2ikVU1rH/ckB9bapmT1TUeq8Xpa0L9evW/zFHZDcil5YGtuv5sYV64mSX6tPyPWMxqS5AFlzAIkU37USZJkWsYsEBi54ECwUiy1EA2XbRY7F0zH6NScNlkayYPVJ2J27QGaIOWRk7JrTB5Qs9UdvzpLwPCSbIBroBtEwAa4OVGc/O7rdjiENKAKfnRDRhcXJNs2JmJQG5FoaTKZZFlgIBOMaHFhtYlYn2lTaWIa4dijUdDSvnhwdBMBJyCEUqheAIWWcG25xh85tkAQMSijd4dpw4uMsknO8EPAZTWVZWJiMd78brAiVjyydboMaZxmaTLi1pLgWgcqI00LDGy2O4GvKV309nG9WGNXaWVp0ZY9DeCx72yMgraSTZ2M2hKOTpJUerSXPSi2a0QBIeQr47ZCt4yy/cxBsnUFQJywII/LwxK3k0BJkn8PU2ZEFrTAAx2NL7HGd1ttxEzDmRbd3znzoHS4x2RygYeYKnh5I3wXpkWM1ulXfk2LqBioQI2xAJ2yMwRudhfcvgHgjoR5fGJiZs32ipHWZYpZWvKAGAUyknnvINOG53Mwjy24EJAQh1w53RWEZXw/Zz7iJIp0eIiOfwY5WgbK/AOYRCvGtLT06KZSKSEa7TP6FoELdVE1TDUYQH/v8LkVWXAcfJxpYxtRiDdYkBJWKWiR90MtHs6ibUsmDdqsaRX5dEoTY8pq6Ms2iSdQBDJAKyJclWc0PYpAZrM0cy7xvQoWTcPybe78yMJHHcfQzt3XsslZ+F02Mg+DABQDp4CbeUbx0T71ThF+CM7Sfw5MxkxtJSdj4xOxvjQIoC0YaO5h6xS4w2382C4MtDzSjTOt5i+bWa0l9vAMk6ykEwwtMUOHlMCtoMjvR40IBwNyeTAM9hz00nOLkUswTE1oMnWs07WPn7dzmSx3GZkWnOaNBmx04+Wgpm2A1pJWbZbX8sDnhRTm9YwtCyj4OCAZojUnjY7ZYtGksI65MmwZ2xHg4igqgIvSMyDU9j4SEhpmTsRYgTaUBsVrbSas7IA6hu5DedoRphWar+VQWUsSpxypQNM7MizbNRiQrO02t9soWl1B0zJNrO8L6b1YeCIC5IimTZgR8oU+iLI5AS7RRqWBRgPsExPjBKKlbZTIG89lhtCpPl7XCk3cGNUmwtM7oa1ffGQ7yur3jjK7ZjU88dMTn0wWsdWD28eFBFD4r2RFkCvsYjJt2yELHEmUSsAFxmqlnhrbbTqnqLCiCNvQmAnVqYlHLnDuyBG1LV6xeJMlyeF0s4XgZNPKsZKBN5gsGt1c0RMn8zSS7wk4/DWAWFQ7ePRjNRTLAzftnSWk1l8RG4+QCLQj0kBEa8HA+ccM3OAl5Rqkv4ayBw15AItBUOcMlk+TuYrBhTvyYz/jdn7wR51bHojQxH+EYyl1n4oEzUcO/tGwR79BHEyDb5NBWr+mtsf7hdv/8/vcubtuc9PuWTsPLB6igTtQzBNsO764kAu45nSK5Sj+3GuP6P2IaM/U2l+24WyFG2ItAWttVO73O+7Yle91O0990eCwHl4mLbD/p59zp3/5ajedPSl0KdKk4XeC4AQ7lhdIQhqti0DRATu/333tkbkxMWuzxopYjHV9pi29hsciGZMkaml5EsshcDf2xP0vf86d/vfjwGXkC8d9BXkAIxxzmFD2iIOTd4Fxqw6TtkgPEpqWd6ABWlF7AAGuDCAmibV1fIHhELgPAnAx02YJUse9tzwdyAx8/jU1OeQy29utcEqQSJ1zryHAlbqPAHZM2+QHoMUBJLo7TNsqmjkE7vaAe+ZU4hQJ3Phzn2nR2M6/G1uxY4G1FATJd/PMg4/ANSZEDLhi2bFRLRU6S9JW0bmwUbT1a9ledUpw72LWuL92qHE3gG+QCkHjEuDW2S+oBBiYiJX74TIumT8C0jKPstLj78+95iiXERGsheYz0+Zbw8+t0kSRPVCgLS6MnUWfvmIAfmbcqw6BuwFuHQcuZzRddjk+gR5jWj03wsVE5b0J9LGdArh6GZezcaMAPDNqcgOmh1i8seVBdAoiN+SoUIc5BO4mmA33RuBeo7IKxt438T40EWvIA2Ns65gT/crkCcUc07iUcSVwx8O7jiujx4RWT1dgz6ZCM9igcX/N7TztMB22LoI1cKuuZHMn40gpLoDXWFxIDcd12gmwoSGa7Py51xyb9NfHI413GTCSdnw9ERyR0/un05Ql3LywRJ9XRkM/P152JAH3heuO29/4+2bgnvrla5wLeVxRy5HHQuWjENOOTMK0NIi4MWp4W9F3GaQCB67MHsQKLBTes7vUBunMQ3vHadMxSg4GvXsRvhcbGfcQuOt6YAZumJyhCfpaoMVkFtuoQM9rm+E19Z7cnPk3ALiV4eCsnjKh6NhK1f1EBkDAmx6fvPsQuOvitdxXgBvSYRJUePLMJeGaK2I5IjP8kMhagAdIa5mW78696li5fZWUVepA4MVVmTZLi+pBoxVi5NyDxSHjborcANxfmidnBLhoPqGZMr0abbw0AE8Ij0fa/AcqCfkzGL5TzYzPwB2bSeIwMF7lxfXqaoCvVVxx08cipMOOHEqFtfEbgXttzCpkxh2aiM2vHNluwyf3OLKiOYwBXHLSZwKuqMlU2obYhnQMZgGgxybAs9NNbLHOR0IXgIduJeAeaty1cev2v/x5zrhxQPPsgmjStD8s/H297IG9YVYCV8uDQoxkQcyffdV5BKbjB3WsxpYo84Bmp8ZELK3E1SGa17oX7ryrfnWzydlcDjmBU6bXx8L6d9KZB30KgVGB1chbWD4L3LCz4/a/+N/dqf9wQ5QKa+9a6JwHkV4NU16S3Y0a8GAaMUwVuCZT8swAZNnsp3lCxewUwdj8hhjeo8THqbw4gTsw7q9uJBX2P/NBt//Hdzvnd8TIGlkUGG2MyJGf2FhlzMerBozBKDeWzSmvYvATs3H5fL9w04nvuP2v/qFzB7KemYd4u2BmbAkYnoBEGw1wU5JK8NO4ziXgdlbESKfXYloCbLyF3GbajF713i0A99ydP+v2Pv0O5xdHe6dVJjvSyJEmisBJNTMm52WHKKM+06S7dggyixaF3yR8s6+kCPBmgBR2nyOXdNogB8TGxpWLwIk9ObbY5lRAlgWwgWXtiZ4/+6rz4bkKVe3wF+vMA2ScWtKWjISFuRVmyHYaybQZKNsA7kfe4Pbv+RXnFkcLVzFi6hQSxUhisaI16UhDM3Y2rdKbdGDtzxw0xqwbCSTbjh7ZBByts/0+NgVMxKztU+Raf/aV5xtTMdn5uBrGVEFrIrYFeWB++G5+71wddtV7NpIK5wJw35mAK8v3DEZMA58nG2nRT1yMQBsHqSCxOPTIZKfBnErLcuCpw5hDA0bqaRv73xRiBPhauGD3oolYm2kLb2Hgoh2kwjtaTKM+Kd+fiMUBHWDa7DlzrcLVmwN3LwGXj//IwBrn05rhr2r9qi7QbmM0mPF3+S8l6iEQgN/1o4hk2SAXmAhCS/v2xgC52qo0s3g2IQ2oaTURAMaNg1ZDEjr+xy5xK/dZIR7+nuhF677S1awDF9sB7t3vdG5nlgrFl9WuYXnuQf5ZhypsF8p69SMo48Dg77EkSA4F8f9nabaSQ5Ybx9tGEbhZygsxLRkTFuoXTgBXG0WHGkvTJb3aK5hRo03ytOYZXgBUs8a9es4qvEB57+gvzn3oDW7v0xS4IzWnKGwnPrRCaJa1pX8UGIjtKr9C0LJBRPcLSYePouJba8qLRqSLiL6pPbimRWIK4Ccv46LzglURV2wfAS7WtKU/JhOSgbS+hlhoWDaa7Etqnm0LALU14L7LuZ35bAbLIanhJSAIyw0fgTrCZhq4kTmlHTDo9WmSqG9Ivo20zXiWkofSmQ09mw/0s3aRM8+tkTkBV4NWV/J0BrZ4HfZGrYdEOqkl2hGo5rLGq+/YjHE/8ga3d/e7yuRMM7UuYuZ6UYQy9gCuac1zvBh7WkzbYPSq6dipjkUi9E6DDG1GB093okjxWS/OcEPhHWMny5m0uFDmrVpPE8Dmpb2zrzwe7s8J8OLZTLwAMOa/q3pK5Mn8d7HB/GuN8L2g1rd0KkzONgRukAoIuDKKWBMxzD5kBFJN81geFIXakvJCMgR8kagMSwyoA3o9AVd5bT/Er6tpS1daEzHm0DJn7mepcLzkcfGBxRZoeceK90jW0TMY8DmmTigjMqX+544776o73JF/tInG/VdR44Y8bp6Pyn41Io2haQNwm3laBCokQyR4CJuRd8txi5UFDVnB0D1amogJSZlgRtN8fHD4Q4NpG4sLBULhGXihpwAXAg/qWq6rasPHmTY0rCUNwt+VHha3bB243NDJ8NDvOkxWl2/HQaHfg0Ar5Yd1mLU8twAAiICCk+34Mm6RI+UBtM0GaAcWFyo+xLluJAL7s69IUmHVCvho6UQufdCqrSGmR9qGK4M7/8diS8Cd87ghHYZDIyO2sp9JOxVTVi1Q2E6biRpuhwK7B4ydKWPAG8vTctaMmjL+DoIWEh1dc7E+jRvsGRWWsJ0ixvQLf+YVxye9ImaHbl4sI/aWQaaMQ1oOVG4yrWaZ2Bu5fy3uOTvv6tu3IBVmjXuEgSWHXrL4l3CJQZG7FM0vWUKEdzXiI0X0KMqNkIUI18X266W8eqfLNORBdMrBxQW9/y05CsGOP/OKC+qP2GPqoKHywrU0LdI/NijCWEvA+53tAPeeOR12lEoXcEJ5Z+8ca9s4KKJTgu8tIOcmYwPly0j2YF15kDSz/owtGUcdQUsE8Uke6HYjohqzHwCuwbbprYoBqb6BFpWrYsZkpxo+BwMSP9LginMVtsG4u/e8y/mdo8kxrLZhRwunby9RPW/HCQsJH6nzGHXSjwQFYe4MdvV+zszxMrmZjLRt/tMcbZpRsO7EXUEe1JVXrmmzr4L5hJ6ItTIqFbjaY9TDY/+6wCtQDl8gJ4CPf2D3hzkorBBS+5zEe7fCuD/ndu95p/M7x0S9MAkjFpNNS7d4zJPdzt9/bu3T7KOwLh2Twf6f/r47+F/3haL4pKdY/MrprCpFqGSa3M6jv98decpzNDbRGJEFgtAav3DLb3/d7f3R7zq3v8+iKm3EWPUb719AZ1oNaxaoZ2UbXggmYooI63v8mZtnqWCAkcyelUhuMi0qlgGgbzgLazNcgNiGVJiB++6kcQVmgvUbNRkH++7YpVe48696q3MLq5JKPFP8eOZDv+B2P/HuWiuBpJp1ntbBgTv67Be7C69/i3M7qKa2/e75r/tf+oI7+bYbnIM7IKwNBCgSVPwwnGgnVuWusZVGbXMjEvgzN18IAjzXHqsxLZcG+Ulzco+ZEoKWh7bmh/22xrgWcO0JaiCKg3139NIr3PFNgHvnL7jdT94RMiT1H9J98XcsVG8FuJ93J992o3Nye3oam6YmDY0BTDs3NLCttB+aTFbQVvLV/VdFTnOMJsCt+kM1qK17uW8v2qtweQigHqahEL0zh9RlYMnzrn73hlkFi3E7Kbm57csDd/TSl2wZuEqjJq0IjrNaLt2xZ7/YXXD9bRsw7ufdyV+8MTJu2eWbVYsBPBQVEgCqRGg7fdXUekVMbEXRGj3PgCDjqu81IG+RjYvyIDhiU+y3S//aEwWiBefNktfM6bCf6MdE44qzd/6c25ulwlxkk/91qvZL17YOXA3aktaksixT0/JgY+Dufenz7tTMuEQqzJo2t4RzC2VCDcxqlzHQsu/NjdieXBNwVoFbG1YAaIh8rImTrl0RtPFd8pwyoIdpYXM6V+H8DYF77s7EuAW4/bLG8g2xgz139LJZKrxlfY175791u5+4vTqOsJ0KStSpZscJGndzxp13+YZV2qGUGp4PBWnQ+E6IzbJ56UFKBPAeUhdTgFuS7ubhutb+MKJTVi1NHP4ehJvcNH/KkhLjwm0HuDNwZo3ZTmGpAvCDvenopVf441dvCbggBFcCSY5MkbwVxv2CO/WLN6ZjRkcKgYxJPNS0gnxK20HBzFSSOlrrV/FbF9YK47ZOzGvSONq5gNhyYP9aaKBhGPSpoa1JhdtxVoHurxNGDyYJk2tz7OsAAB/vSURBVLOXuO0CN/afARZOYn18/49sxrh7X4rAnaXCqlvIWfYgTci4IpMMamQOGDABdkQFXH6vP3PTQ8J/K+8ugM3/oTUte6dmjPhIAMbxw/FQnWjacBiAO0/ONtW4beDmEKc02ZaAe+4TdzifsgqqOi9EgTpnrsCYgXvgjv7I5RtJhQxcfSCI1NvAoUJjVpMHHKMD2YPUYVjuGYBL8ixjZ4jFnQsFq+ZMEzeueE1TWiQJIq4pTfWzVNgUuG+Mk7NQq1AdlEwyZ7fzcCKxJeDufuIONy129A5qsxY5te5BAy4GLZNpiWFZDUkGsrqwSJDQ8Dj2CBeSbWuUVlp/ttbpxLhwwhXuIIdDhJeinQtISAPaR3uKoDzQK0nqPIfFgwFcvn08GszQfvPkbAtSYWZc7jjJbnq0+KpjAO4sFd68QTrsCyEdNp09neBmT5BUc3A9bVwJJUTIC2YGACtIkL+3gjkBdyTdRQYxOT37Liz1tMZEJ1xmMnRhWRIfa5gqr5j/ujXgzlIhTc5KDUB+U6PgYzlr3J/eWOMOAldvbJyzChsCN0iFtybgijwuIrIwbO0KrwhbMidQErTnkOnvWjZxh/ZnbnooOLbK8gyZ8mrk7EgDV9K0XAixmWSEU92efv6179pQ477R7d1N0lEJryXlxZxRhLitAfc9fOVMTcZQfncG0NIdffbl7sIb1mfcCNybuufjlqE0vufMCEVEqQqD7jaihHFAVEBe+NM3PZT7AAvd6XjJ1Jj2woIV4kAtLayN0BOx5jljs8bdMnCzCIOaVkaRWeNeNmcVbtsgj/smFxk3Lfm2zqbN6IjyLWUVtgxc5TTVc+OhlpKokFPpjY2McMojUZSv72CA52QWoo8ALvKKGi5tabBd0DIBb0mLLQF3lzJuBgViWhHi5lqFY5sC94MUuEb0YrGWSJeQDtsicK1PmI7kaMsYEaxIgDL7YdByojIiTWLfClzzXIFYexAKpUyPLNICa9NWo8XWaAXaYhTR2ZBVeKc78vQXGDWxCn3qF2c/9PNu9573kHQU0LQgdIdfHextEbg0q5GbifOgpRNbA+7N9HxcMn5pKmIc86kKX6BE6KS8yNhq6du+NwIXApJQPllNYxNGqjkJLAZPdIT7+bEexmFq5wee5fwjH08OZ7bCTz2SiKJ3ef8fuYNvfCWFwLHK+zxgoTrs2T/ljr/830SpACcdDedZLNzZD93mzt39PlEdJvpqRYEHB7iswaY8KExGL+8WzOBa7snn3WDkYf19c/70jQ9TJs8aL8CgtQQsdJ+tS1AY5Cmv6Opy/1WjdmC+YVq6sAuBnQkb+m9W/nPy3zHOh0XSR0wapsktHn6xWzz2B5PBUR/xlp/c14Nv/E+3/NZfiAHtMG0miwLcf7d2OmyenJ18682yHjcCqdQd0H4hYjDkgULViDwgTtuM0s5h4M5LrIVarQHBQr28r/wHuF+xSAVFeS0I0ZwOeEhlK4MwgtR2FNk4pUNJupqW31suD1tn5AilvliGp3aZdz6UMuU2KGoT03VbAu6pt97sptOnnMulIDMXhNEfmYjNrZJVgQZexNYkKAnNRRcWzmOqmDJu2GqjcpmlIVX/GFVEbKwa8kOsL/OcdQvwbEeGNlBse1sbccdCwG3v5I0mJO+WABU/g3DWnp3nMYISgbRtTseFydlmjHvqLTe7aS4kX+Ro58O2myI/ZX/LH7g0iBaZK8wkCyBbrfE1SYZLAdyyKmbStAWKKHrCbU3gSS1Iwm+hQQEMaDgRtoutbNCGx4+cLsNHrOSRi0Pn9nQZNQ0kG8d+2aSbyEGA9F5JBFsE7vLM6Tj0wymvyLQQ3K0QX8IikITMYREG+OnvgXEZ05bWIMq3j/WpTGaFCg7arPO4g7ZFeQQfB20FFAItZkedp+2UNBZ/bLN5LcgxnE+xEWpfo7yQ3r8/55G3wbivcMszJ/mh2tLZGEJr7UHdrNifTEWUA8IJg9o/LkrWCvvTN/4tHuFb+tDwJh5+EXA7tQeQxfSg4vNX03UtUORPcaDySKPgw1xybEUVYTtuWIMIGNMMgnYOyftzHvlFG0uFk295RZQKQ5qWfCTRlGW5Q9LJUZTsEEF6lC5wD1IhAreGUmDgTUsTxWDzAe3sps0mhTXDuWf2RGLdc7zyE2Nbx5jW3Gbf1X2tXcIGAAJwZ8Z902ZZhRWBq+RBq28getfLB2RTmtNoTkrAfXBWxDTLRpiBjX9wCZgP2Nhp19rbY6cNYMBdqsbEwRogoemVUw4yGYvO5YeGNJuBe+nl7sIbtw1c5KTcfpHpLEnIJ2M4cqH79fNaW4n8qRuyVLBZi0646MCYX8UJHQOdRYPY1X0MSPnqmK5p3FvCCARu47yEpr4jjhFFespzG4PYqz0IjRxZrROyaX7trHEvvdw9ZKvARRmAdfO087hlIxGjhgpndu4UlinWCefpUf7UDQ8Hw6+32kTT1Y41WZrkgWvTpcbphIrQcMurka6Vnj7OtDGixfvbkz09sLgQSAMtRxvtbBS4Y5ovtHcrwL3XBY0753HpsRcFEegAv4GJWJJ1Cp+43JWhOyaR+1EPALc/O69s1piIJX1DNWLpCA4zbLUrhwndeStMcaeK9wk2E5OnAiZJAErTin6mgWX61wzvQr4U3bfaEnO5LdZGuWl/PtfhhZsx7hcTcOmBIAC0tfed3djFLiPRmzt3G1MKk9MgcEd1aUp5FbShIzQbbFRGx2LbwRAPswdtXcXDToNVBPhVuGrJAwIKrWn7bJuLnIJ5Z+BetiFwv3SvO3kbyirIxQUEWDGOsW98S1fTmTm4cQTHhBHezKTCUPZglGUB5ff27TfrIpA8qMbralriFONMa7ElYfciLwZmySZwkbYUAyvD7IMGXJny6kc4Kn/gbmHm3Qg/0XZas9p2qcCNoCJ6A7GsxZYogzBSMENYJtCI8ZGQrD5Vz7g8MPeHgfsMI8X+lz9iTZt/S+ZncpZtHB00lt1QOVWk97cB3CIV8p4zuRo2NvunCwt1REc20/Lns3qTKteSmTngE3DlZAwclQ71YZoACRT0vU47gK1pG2F7marDMjHmdhjfHGPNbNQf11qKaCwO0NGJF2CWhsbO1VjxXZbEEoXkz7ncPeTmDbbuBOC+Mi1AiOyBOd5AvxLD1v8cm8TJoaPRsHUclz91wyPA19NX1LTp7XqyMhY+zexB03gLt/P3nuUWj3qiqsfVbNrKO1K1icJY7yw0cr/Z3ngNGlTK3mzQtAhOD8mctnRHvv8fuvN+9PK1tw7tAeAW3+8dElO8mcum0uyhVOBIsQ0niowxf+r6R5Bxrg9KGx7SL+SA6lpauL0dpz/YKALQVrliHqocP15y/rXvcEef8eMEuNZo///6+zknip1tpMczcB8gjFvm1D3QBS9EgEUTTD4PiVegKjIRyTAJhM8czM8gwK1hkdODMRlT4UHOAC2D5t9bjcfF19Qxwgx0seOOX/ef3NGnP39kjA6vARYowD09H3qX9G1vAl1ChwRuf4IZNVej9LExh4mvjRGcABeB1gKenIiN3is7ZkzEWjpwbnzu3CFwN3bGCtwzjT2FGJDG5Nb8nkQsfB2v16WdK6qEOJU/df338DlL+cmQB0ysjeZpqdbtZg6KzMqarzY8d2feObBwx69/xyHjbgDfCNxX15WzFuMJQuGXdspRy8fJwcQut589kF+HahYKcMt9kPHQ/rCkVZjhOnvEkr7hE5X0gEaICsAtuZKUZzwE7gaQjbcG4N726phVYJkYnBExT3TshPiYo1XPjCnDwkpC45beYUkZgNsG7fyEWgSeZ3XQak1RbyWZBzRtQTrZanMI3O0CNwIrwUjOVxBJjeZ4O9+DMCN8axK3cP5klgoDTFsEsjRZR5fm7e9aFyUvszw2r6aEIxNFwcchcB8M4Or9dDwnzjdSDkymmh/1E7KTqoaQ4VBEWHPN/uR1WeOi8BAuzJE6PXcke0BmnKnaB8sDOzyE64s2AitOM3Cvm9Nhh1mFdRFcpEKpDuMTsUi/YvKd5V5Dk4ZLzOq+fvbBXowqkX/yJ697JCBClKcFIOukTnKJGvWkYuSBe+MbQWndfO9iBu7bD4G7Lmqpxj192ihr7JcX2vn7+cSLxlctgSwpMlQhkhBX+hsGLqmuMjVtL9/XqqfV0qJoK5pBiAlxWZqYPPYQuBtANt4aGfc1sB63eeAgmTixRoTBG015oWiL7tWgDXdqxm1sIS/UiXK86+ZpReojhRnItLRO9hC4WwDuH8asQpYKhFBUuktpPYyBsXMVNGj1uXQzYOf98hK48b0EuPZO3Mh86V+Lac2Om/fy4vFinIHdrmFydigVNkHv3hclcOVqGAFYayKW/ja+L1Bo6QCwsXWD3F8O3NIAROP9ukxW6Kws2ikCL4YZAG1oXlqAeMbzNhm7v9H3BuC+ec7jzmWNqxe8yPmKwnY4WD/pXJH2yj+GSrgBTcuW/CvjysM6UFmjLQ/Ce9MXCWHKC6bLonOklEVKIIxuZ5lv3UmMewjcdb1vBu6JN7/aOQjcNN4dps27eHNMZYeENOTFKhMxtO3fn7z2UbF2wUpvNEBX1IN5jb24QCdh8TnDoI2zVb9wFwSpcAjczYD7mnIgSJWE/ZRVwGRvi7qBKRu0AgcN6RmBW3K1eMWEG0auIxuyIkposB+j5gXjQf3W2bJop3ES5uHR3h37x1e6nSc9DZyYWFsMI4BigtbQo0hDrjdf0LlPvLI4cvl9inrm81GbV3inX7j9//1Vd/Y33++m3V3yMMS0+rm5UgtiwyLBzGOQ6FT2IJlE6u48OYvAxVvBe3WZ4ECN0JEeS5cjkcaZFgt/ckynFdIMbVXb2dDUJaQYgOjaZ5UyPvAOfUhfncwa381lDjCSsmShdhC0LaYtDcA2w6fao5SX4SxpTBLjVhasHlQYLzelLAdGXYruyUrHSJUkULMshZWnVYRinPAnAbv2hk8UbdBhHbbWz85Qm8RCrmIQpd0YGOV7UPQiX0EKl4/pUnMi1tKzaexKmGfjI9+7yvjb8oAHRm5L/8C1fycMNWvzgKfKWR4AvIBeAnprGRcydWlw9JcsQWqvOCAAw/L+dfZClft72Q3ECLmKDYCuGYmSqXofCmGDJPaIhed3nCq2obC2crBGiI9OKckKOTtxIIIALC2a516QI/bpe1IxeQFuk+IZ2o3UyUCVVxMU7WqjYSMn8Ofrc8ubmqwYuKG3ufuDY4NybYX8poE2ugwmwfTLRjpKvXtkN25+S2FDnTPXJJADcRFJNfUzuoVcO23SoqLbTdCGHsOT0ZPzBMYtT+zWD4A0Wbm5X0wcL0XhV3upmcxuhbOWntUTxTqQPZbtMVFiI9W0DIyATJzHLhFEaVZiE/Jg9n0OOjfp2qUCipFATx6UXSfCASm0KQYEPNdg2kju1KnyL4gNI3DbIZoc1oAFdy97UHWIBC3SZMjAeBCZRiRM25cGIJxB8LbbV8ZuKC3Ebcfw0vrMKGHbsQOpcd/GisDB+LZ2+7YcepoZE+3kbDOtiqyl/7xt/oFrMuMaHpVutLeQG3oujWr3fFrGJo0Dz1i4lNKlhkXNeiOatqdnRdilrNLdxo21cHhEAqxyNPb8GqWKmjOjnLBLKnipcon0A4YHfn/zIG1jPMpjIRl25YFm2hTNBJE7/8A1F4Mu0A5IQU4eMTMNt3rNPLCnji4uoHc1WI9IgzHACjYKN422TTNZ6/zWGu/sKBW/I0b/YYek8qACo00YuHjfdnjZEnQKOLtGyLISfQygWcUy7D50OqchXwFwA0OFlTyac1Ne29XDuUkjmtZyDgO0gnrW0sPBwAi0CaCNMBj+1DrzoTJibmlRS6FH6SMh0NnYSNZJWOWHAflCSkohm7dCvLkiJhwFMKpdq4JLE3larvv8aMv0XgFcoi9N3TaWdomdGGWzkYJlwnjJ8Guddg21rGA9hShp1JEMALGlpDOlaS0mlCmvEdnTOenSiJDFX/KsnaVIUfvqPsfqazjXrKKKkIfx7zrKS7MFvihZBSoVClgNBoThgQ9q1rRmPa0AhQYf8jwNWjOP3FvNCr1HTsWYLDElCPOwQF4OrL4vPBDmaVV/p/q1PLptCjGtHPD4s8nkHabNk2x+f5vh87W4ymtuSv7yH0+X1qBpSB4gYPOhiPMd/oFrHl0vWW3XQnKI2jH+Lsq2yhNDu8eq7PHgFPzVsGzWRZRLWlGAi63ctxrq6945khi326bat+I3xCog4oJBs0CbpKwUoYKQrrfbrH0kUsEAbx+ahAlbpTsZBqwon3CZ+8aBS8IvoujxlNlIyqt2ooLdCpc6TGvQosUP9LyR7MEoy7ZBy9o4mO6i0qrYBQJvhGkHGDqFaO0U6Pn8d7V9KLdv1x4Qh4yRv6TMjHrt1H9JwJVxm2wLBpPuww/3rjbRWSmcEYkC70NhhWkmJA2yyxuhqjCCpemlfBGSKXs/BG26l7e7hFXmzEN9Q9KuHeJrFBqRFlj64AnqGhOxVo2FOPC5sO4sFXDFTnsJN8X69P8MYIwUvMBGE6OX2DdUZKO1aRl4EAkUKAD4WqG2OlSRFJxpUXi0IovczTwAPMFG8Q5iJ9Y/g3zUSeDyvei+egAdj84StMiZAVEwsZoB0d506R+4+jHhNjWGAyFqlcWF+I5sWrAOTVi1yM1STs9BW7A8NBFbLRLQd0cYGA5TjKYHJ7bPp7QXHdoUDpkQBSmv3kocfzfW3C2nJJqRj33oS0yGln/1PzNLmSWmtKsEP9mrY5OovaztXDHKtaJrAO5KoE0fr4olZauBYizMa4/nHYdhVqRTwjX6OPtsuB7T9pwWOFkBfH6HtbgA5AH/1YA2Je/v34s0ewMUzKnKvRGz4X87KS9hG9i+YizM5vYnFeq7/YnEuIzyjeR69bh89Xi9KvQ6jWRwYLAd4hArVO2G5Mto+G2cd9UDbSt7oPoL5AGyidLrnAVjnwf6BhcXkHTRNsfFMgAHUB6OMm0FZjUDllYAuI0kN9NDI9mD+NLciLi1jRReIBC0PHY4hIa2ZV+p+B5e7QM6jMVUNLC19oCRgAJd/Kt50Am7WbeD6gI1uAz0oI2t/rfunQtmwuG2vGetZVyTafPOX4O02gxdHaACN4dHFiq0J8cHj6SV8ApOWEk2NFQ1DDvSPw50oy6Cecfgd3vRztFWlRt/B2A8xbTZkJIJQS2tKrkUoAPSpUaw/kdCYqTkJMJY2nDKONaWdLFTXuxxQ5q2UevLQnW1iwYu8vgm06bOKQ8ywhnybK4j9P63XmV/eaZRFyFOHIyjIcGxWlqIRBE3rZCnpWGgAKNhu+gwDWnAUaIK3MOjW5NYk2mj45knJrIP9RrSQmLJkEH1cEN6Q9t5/YmrHwu+uiPYsgkMrEEboSK1jmuXOKDohHNDwCsmGK+LoObJA6sCjXo+b0cBbm8ZV9iO2iW+UzKm1nTr1tKaac6OHi5jAYGW7Fz+xp2q2pHay1pcWD0K5LEDwK0PC40wQWtV9MuVFOQ5PARV+wCQ9lJe4eYx6aJZdmQSRtpKhXMALP+bFX5nPZt7phzaYrz0ewpaBQqTLfO4rTYWNYokDLDnr1lLC48oWC+K0MDsT1z1WOFXtdHRUKOgiIPIoz7SR5hRIOP1QmgIg6PtE4zZ+kq3ofmyt+McrdCDpEMZuNU2I1VeiI0szTkie9S9Kl1YzK1OJWqDlvWLhbNGtBRjq0jAZPT4TAHcCrT6XCN7QAaXhnkOQAlcBdry0eJ8XzwkZORbEtlCxooYawgIZ0O6DxveXFxQepO3DYfudkQaZOiMnWiU1m7cFkuPTMSgZpbRBxEWQfSwZscROHQ2Mi6XB/E06YZm7IGicYJNNGxRKoMrdmhw12daXR2FmMwA7cBELKJI1tKO1THTnQv9KKSjSO5bm0C0/MEZlb48yPKI+cNgLTeTiL3oKivEMnDDfeXMA6QbLS8aKQLnTJvDAjVu/O9+agfX0iaF0tPDvS8agvBU5QHcQp6YTh4XhTTtWN9wcXznKKs0dmZtc4NlC49AsJGJWBgg4SiWxlcgrHUvhpSs8tp4pjzbzp+46pKI2fIyYxm3AQoVzpBGJBetVYeL9SwEDpqE5ZOyNYvJAeHRh8qg/KHoGvSkM4O6gzwQLUZpRCBcoM2lT8jHkE+PMyZDY8FqU8K9ot5hlGll9snQtC1p0LBLQbOqW/BBKuy5yR+J/VsNtNqx2qmdzLRUv+dQAyoqRE7SOqwjhb2WdjM/oEKBZ0mDOWkhC2aUVmeZjbYu5WE6a1JoS0r3xWjCscKNqO2N+UUB7np1B2U+wirR1gCt6p98hlkhtu9PXPW4b7tpekRT0zKkNcIF8h7GtMjIIzNsJF0QYPXzYzgxjFqkifH3/pFIOfIV4Cq2Y4wn2iekCTNfb3m6PHcctKSxkSda8kA8H0bVAjwLtLy/MdLSVrD5jviDPMaUveM7/q+vvOSPvVs8BbKgYXTaibJo0GC8ONlDE6yG0Zk3GsU8LO6DZ/U+WWSyVXw5XhEjg0HkFQcFmg+g/sdnKUD1loDTuJgLEx0mGzpdxgSu7EefaSuMUKQyMJBsC4nATX/iT1x5ya9PbvFPOHA1C+Iw3xggAWRFxpss4w5M4rqrRg0mDm1dhuR9Z39ZjAR9aQAijZglF/t3J5irTGKN6KgGo69p4fg39ojRgvaIJp8OLGQCGy9RJ80OAR/Gxt3lT1z5fbdObnqzc24nGs8GYx98yKMQ23aYdorZ3FW2tzOSaR7rn9+NBrWeeaAjEGZaFZTW3tHRtn3tHxofdG/pXyX0FGH4OKI8eLxFRgK2JG9Fq1aUUwDSUWggihwsJ/cv/Hdf/vhnOr/8qHPu4hgf5ek0cjWsYaTc01KUk79LSJiLe2m6gwLeAm1nwNLIdpmWHLNJwVls2tuNG2yEKrySXTqSiebM6/uxw4PsCGcuTjRCI0rHRPZr11PX4VxNHkA93LJLrv6DbMGwMV/5zQO38yL/zZf+gwuPH/nrDzvnf0Ks1zIaV1oDNYTpkrZ2yTgrnzRkz1uvYGbsXIDarvzKIJUhYC0WHFlcwJqe+DYZprYmDu3s1SIb1VosxFsSofzekBalpfZWm6ipetucgGRKztfbfl9OsHHut/z+4qdDS7/78u99qXP+Pc65Yxz0sSNDoE254AACMRELv1tb044zLawuY6Gbgza2Nf8z9ogpLSCWcY1CccaWBBiQjdQ7BIBSWM6/tccDkYVle1lPq+/lZx5YoENLzOJawzGi5VEtLopA4Xf7y+Xy2ks+8bvvCz+duO5Jj5x2z/5X5/xl5WEEwbbXElAVFEiwDyx1lo6tybRVV/FwaUx0FPHMxhs6gC5OxBjghx3SIAEGWgC8BFrYZnBvNUBiP4tljfcqp8g4MBh/nJCAY8C5iARt/dk7/wd+37/w4k996i/Lb7/7su/7p5N3t3vnLmBbnLmO0uHNMF69cN08rel1LCjEMArYoLPSZ8sDFOLn5wNduwJo+8BDfYjRqzoKsom+rxBNC7Qj42bZlkYpJg9J9Oo45CDgaQXb6YMDd+3jPnnPh1iPv37dYy64cPfYrzjnXtY3MtZk6r6B1I5Re4DBSIuByASQIVmFbhQCE2u2CmaE5t460ypjaYdpzrAb4Xc8T8ulU462SkJpQJQQr1ROpwA/v6OpaUHfls5/cLFz/vWP+djH5s9gcjF64p8/8UnLxXJG9FOZZGANbzMhCzWtwWnJgx6TldoKCUo7zJSIlw6fy8u4ZmgMI1KlQR2gxrI29SCyvq4Y07Jnjm6TONpARj0hy1jfeCERkU60YCZlqFuTqdBhrJlRJVlvgSk0hETGYXKMfb/3YOFe8rjfuvvPBKdXi//VlY9/rp+m9zq3eEL4bRO03GOLPQeAZzItrgnlH97oz7Dh+n3oymD2II64XFxQg5mAgR1GsfTAwgkFhXKqBuCH5IHB0uVe5gHtmmgOPBSBgYQhwBVREnhqtqn/2r7zV37vb3/qM/Qe6FLffdkTnz/56e1uchG81OMZmEX6I4xU6TDxdjmwjVpaajz53sJIwp8kI4msBgPQQD1tbAKo9OpJnzSaYwdN48kKH1Bh+x7wFI3JzIEGUxxO2pb26TJqeMzic/7MzLgQ8AJT5B33T37nlZd8/JMfl3axYoH7q5c94TneTb/knH8qWk0bK020JhNrFIH3wowxqNUIMXOg9JsaCSYPkr2xpmfGFO9XA9Rhy1iW2JM+OQbWsynCYyGTCdACeaGYlhCDciBgf72ShhyDE5mKIsz+DOz3Huy4Wx73sbt/DzmzCdz54u++/Ik/sJzcG72bLnfOX0B2Log1/E7Kiw1ae7WmNrKChXYWAg+AgodplO4iRi73V2nQ1qVigIDTMFA0QFsYT7Fley5RSQp981fWHuhJWPwNjZhGuE+AVc0bmoQlO1kH62HQnnaT//WlX77pkt/+9FdwBMKFnOzasLK2OPmCaXKvnpz/Ye/cUcbspm6JhRWZIzbZiavDGQqhIPTmy1bMHnBjDUzGoESAQNDnma37OabmMmmZiCX/1cDlQ29p1M7ignbIzBfRhKqQSIwRd/hdN/nPueX0Tnfs+G/k7MHawM03PvDSv/uofeee57z/Se+mp03O/W3n/E63AJywmQIE8wAGxmKAsXwfN0h5ZW8ixt4PFhd65YUEsCoSaIrSS+jDs3YOvPCuXi0t6xvhD1R0bk2m4a6KyqIqOrIBbh8TmoB94Jz/lp/856Zp+V+Wx93vXPLRT3/bAiv9fVMqoAcEBvYP/NByWlzm3OJZzk1Pds5f7Nx0kXPuCF7qNHbiio6q9yWDKr0IBkXei2sPLI9HW25GmXbFLfmFiRIALBsA+VG6rYEGvrWAn6/8ySxNRCwtnmmtpqVFk8i4lZC8c3uTcw+45fQN5xdfnvzyD5bTkc8e3Xf3XfypT50aAWy+5v8BUrIHNHvQF7oAAAAASUVORK5CYII="; + + const defaultLandscapeImage = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIMAAAAcCAYAAABCrQzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAARPSURBVHgB7VpdUtswEF4zvJOeAHECwgnqzNDnpifAPQHhmc4QZtrnhBNgTlD63HZiTpD0BLgnIL1A3f3Qqiiu/0KcOC76ZnYkSxtLWq1WuxsTOTg4OKThJR+PE1ojvA/fPHJoBXbIwUGwSy8ASZL4XCjUPc8LqQHIHEB7TD+YIp5LnOLpcBEw7TP9Ep5I+vpcoD82bXWj1crAAlJczFk48xLWgOlE6iFtEDLHCYkyWnjHFFt8F1wMUzxvmY6kPpJ3hEwRrQFZyjCncuE2Ajk5p6RPmG+1Y763TDfrOjUrAJuspB4x3TEdkrWhojBDecRabqQe0waRpQwz7/xrj7YMBScMMOY1YL4xK8QZbQ/6UoY8r/clPECP+WbUANrkQNqKcMkEhT0gbUYh5Fj6BqwQ17Q96Ej5swIPNaUIQCt8BtsBZODkD1MsEGDIfCFp36A0nJUrRzF1tvBqWQliRZU8zryK135bLIOy6nd5TLzogIszKTMBJWCClXlgmjJN+PlBHLgs/vtEY5LTP0yeoKTNNw0W60WyCFxpE+G5sN5n456WACIOmSd+NxHC2q7N3IpQyTIkn94ERL9fL7bu3LFvES7yHfOiFgf1zr/n3ZPPBeZxm9fJijCmYkBAXdKOGkiRNtPY1M6W+RuVkYpGsK6Y9LoUaX8KCtpLh7M2qlmGhD33hE+bTf8oRx5fLcDmG1M3kNM6kOtjWSjS1uMVE3wOUGy9u0MrAteOJ7CaQ28ReO4Jz6XFd2DxHFQZj+cM5R7K442840h+jxDWKH2hL9WKa0LuPFgYoxCKdNw9MSacaVRROS5t6yEn5crq71P7YK4ZJKQC20fgOg6SWZ9fpOytSTphUbwQOIpD0nF61+r2hQbiRJ4VOE1hRltjHnxN8KWc5RwIWxaQW5TB064MpJziAHXRcF/okJ4Egn5FOvTMesdWJtSeC5GDOe19KrdsBZbBezxJFjIcDA+alKT+3dzJ9eo3AdnUWyETTiE6eFQScQb/q42vgJjKs5a5/btVvH2JGkJqEAjhSOcYMjcYVoN54DydShOUYhPKsE8NAvKQdDzW+4WfB/RMtMKBlIwinKRRibe/Z9XrVgSV0+5T8zA+z0mWfGA1kdcoeUdrkk5mgQHTVBI2ynTKYkfSD4Q1XhHmOlSJlZiS5BWUVNFmoTLaTGgKOV2nZIO2z9I+pQK0JbRErGzCI0U6Xr63snzIuBnzGNNi3L4qxlZ9KNlKjIcMZkAFCbCaYU6/L3mWx1wLGiSdbtYMBxJ9U2ueJvK6Khpgl7OLS6U8C1FDwiYPuAt5cRA8fIIsjxmWAIsd1+k44o8jHhfKaL4nMN47xsAGRLSZ3AQyo12Zg5K2mTVPKGpMWj5dWgy9I9L5lahogNZ+AylZN6N8cVGadQ1jbmS8gjmQzGGew4M5mpR7XPVwtPZLpyb+6vUa/Ht5mTnI5ke0JNwHsQ5/4ZTBwcHBwaEAfwCepixlSVZHRgAAAABJRU5ErkJggg=="; + +const PartnerHeader = (props) => { + const { + userdata, + globalUrl, + isPublishing, + isCloud, + partnerData, + setPartnerData, + loadingPartnerData + } = props; + + const classes = useStyles(); + + var upload = ""; + var landScapeUpload = ""; + + const { themeMode } = useContext(Context) + const {theme} = getTheme(themeMode) + const [isDisabled, setIsDisabled] = useState(isCloud ? userdata?.active_org?.is_partner ? false : true : true); + const [file, setFile] = React.useState(""); + const [landscapeFile, setLandscapeFile] = React.useState(""); + const [fileBase64, setFileBase64] = React.useState( + partnerData.image_url + ); + const [landscapeFileBase64, setLandscapeFileBase64] = React.useState( + partnerData.landscape_image_url + ); + + useEffect(() => { + setIsDisabled(isCloud ? userdata?.active_org?.is_partner ? false : true : true); + if(userdata?.support){ + setIsDisabled(false); + } + }, [isCloud, userdata]); + useEffect(() => { + if (partnerData.image_url !== undefined && partnerData.image_url !== null && partnerData.image_url.length > 0) { + setFileBase64(partnerData.image_url); + setFile(partnerData.image_url); + } + if (partnerData.landscape_image_url !== undefined && partnerData.landscape_image_url !== null && partnerData.landscape_image_url.length > 0) { + setLandscapeFileBase64(partnerData.landscape_image_url); + setLandscapeFile(partnerData.landscape_image_url); + } + }, [partnerData]); + + useEffect(() => { + if(partnerData?.image_url === undefined) { + setPartnerData({ + ...partnerData, + image_url: defaultImage, + }) + } + if(partnerData?.landscape_image_url === undefined) { + setPartnerData({ + ...partnerData, + landscape_image_url: defaultLandscapeImage, + }) + } + }, []); + + const removeImage = () => { + setFile(""); + setFileBase64(""); + setCroppedData(defaultImage); + setPartnerData({ + ...partnerData, + image_url: defaultImage, + }) + }; + + const removeLandscapeImage = () => { + setLandscapeFile(""); + setLandscapeFileBase64(""); + setLandscapeCroppedData(defaultLandscapeImage); + setPartnerData({ + ...partnerData, + landscape_image_url: defaultLandscapeImage, + }) + } + + const surfaceColor = "#27292D"; + const inputColor = "#383B40"; + + const bodyDivStyle = { + margin: "auto", + width: "900px", + }; + + const appIconStyle = { + marginLeft: "5px", + }; + + const dividerStyle = { + marginBottom: "10px", + marginTop: "10px", + height: "1px", + width: "100%", + backgroundColor: "grey", + }; + + useEffect(() => { + if (file !== "") { + const img = document.getElementById("logo"); + if (img) { // Add check to ensure img exists + var canvas = document.createElement("canvas"); + canvas.width = 174; + canvas.height = 174; + var ctx = canvas.getContext("2d"); + + img.onload = function () { + ctx.drawImage( + img, + 0, + 0, + img.width, + img.height, + 0, + 0, + canvas.width, + canvas.height + ); + + const canvasUrl = canvas.toDataURL(); + if (canvasUrl !== fileBase64) { + setFileBase64(canvasUrl); + setPartnerData(prevData => ({ + ...prevData, + image_url: canvasUrl + })); + } + }; + } + } + }, [file]); // Add file as dependency + + useEffect(() => { + if (landscapeFile !== "") { + const landscapeImg = document.getElementById("landscape_logo"); + if (landscapeImg) { // Add check to ensure landscapeImg exists + var landscapeCanvas = document.createElement("canvas"); + landscapeCanvas.width = 350; + landscapeCanvas.height = 120; + var landscapeCtx = landscapeCanvas.getContext("2d"); + + landscapeImg.onload = function () { + landscapeCtx.drawImage( + landscapeImg, + 0, + 0, + landscapeImg.width, + landscapeImg.height, + 0, + 0, + landscapeCanvas.width, + landscapeCanvas.height + ); + + const landscapeCanvasUrl = landscapeCanvas.toDataURL(); + if (landscapeCanvasUrl !== landscapeFileBase64) { + setLandscapeFileBase64(landscapeCanvasUrl); + setPartnerData(prevData => ({ + ...prevData, + landscape_image_url: landscapeCanvasUrl + })); + } + }; + } + } + }, [landscapeFile]); // Add landscapeFile as dependency + + var image = ""; + const editHeaderImage = (event) => { + const file = event.target.value; + const actualFile = event.target.files[0]; + const fileObject = URL.createObjectURL(actualFile); + setFile(fileObject); + }; + + //console.log("USER: ", userdata) + const orgSaveButton = ( + + + + ); + + const [imageUploadError, setImageUploadError] = React.useState(""); + const [openImageModal, setOpenImageModal] = React.useState(false); + const [openLandscapeImageModal, setOpenLandscapeImageModal] = React.useState(false); + const [scale, setScale] = React.useState(1); + const [rotate, setRotation] = React.useState(0); + const [disableImageUpload, setDisableImageUpload] = React.useState(true); + const [croppedData, setCroppedData] = React.useState( + partnerData?.image_url || defaultImage + ); + const [landscapeCroppedData, setLandscapeCroppedData ] = React.useState( + partnerData?.landscape_image_url || defaultLandscapeImage + ) + + + React.useEffect(() => { + if (file.length > 0) { + setCroppedData(file); + } else if (fileBase64 !== undefined && fileBase64 !== null && fileBase64.length > 0) { + setCroppedData(fileBase64); + } else { + setCroppedData(partnerData?.image_url || defaultImage); + } + + if (landscapeFile.length > 0) { + setLandscapeCroppedData(landscapeFile); + } else if (landscapeFileBase64 !== undefined && landscapeFileBase64 !== null && landscapeFileBase64.length > 0) { + setLandscapeCroppedData(landscapeFileBase64); + } else { + setLandscapeCroppedData(partnerData?.landscape_image_url || defaultLandscapeImage); + } + + }, [partnerData, file, landscapeFile]); + + + const alternateImg = ( + { + upload.click(); + }} + /> + ); + + const zoomIn = () => { + setScale(scale + 0.1); + }; + + const zoomOut = () => { + setScale(scale - 0.1); + }; + + const rotation = () => { + setRotation(rotate + 10); + }; + + const onPositionChange = () => { + setDisableImageUpload(false); + }; + + const onCancelSaveAppIcon = () => { + setOpenImageModal(false); + setOpenLandscapeImageModal(false); + setImageUploadError(""); + }; + + let editor; + let landscapeEditor; + const setEditorRef = (imgEditor) => { + editor = imgEditor; + }; + const setLandscapeEditorRef = (imgEditor) => { + landscapeEditor = imgEditor; + }; + + + const onSaveAppIcon = () => { + const canvas = editor.getImageScaledToCanvas(); + const newImageData = canvas.toDataURL(); + setCroppedData(newImageData); // Update croppedData with the new image data + setOpenImageModal(false); + setDisableImageUpload(true); + setPartnerData({ + ...partnerData, + image_url: newImageData, + }) + }; + + const onSaveLandscapeImage = () => { + const canvas = landscapeEditor.getImageScaledToCanvas(); + const newImageData = canvas.toDataURL(); + setLandscapeCroppedData(newImageData); + setOpenLandscapeImageModal(false); + setDisableImageUpload(true); + setPartnerData({ + ...partnerData, + landscape_image_url: newImageData, + }) + } + + const imageInfo = ( + + ); + + const landScapeImageInfo = ( + + ); + + const errorText = imageUploadError.length > 0 ? ( +
Error: {imageUploadError}
+ ) : null; + + + const imageUploadModalView = openImageModal && !isDisabled ? ( + + + +
Upload Partner Image
+
+ {errorText} + + setRotation(0)} + /> + +
+ + + + + + + + + + + + +
+ +
+ + + + +
+
+ ) : null; + + const imageUploadModalViewLandscape = openLandscapeImageModal && !isDisabled ? ( + + + +
Upload Partner Landscape Image
+
+ {errorText} + + setRotation(0)} + /> + +
+ + + + + + + + + + + + +
+ +
+ + + + +
+
+ ) : null; + + + + if (loadingPartnerData) { + return ( +
+
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+
+
+ ); + } + + return ( +
+
+ +
{ + setOpenImageModal(true); + }} + > + (upload = ref)} + onChange={(e) => { + const reader = new FileReader(); + reader.onload = (event) => { + setCroppedData(event.target.result); + }; + reader.readAsDataURL(e.target.files[0]); + }} + /> + {imageInfo} +
+ {imageUploadModalView} +
+
+
+ +
+
+ +
+
+
+
+ +
{ + setOpenLandscapeImageModal(true); + }} + > + (landScapeUpload = ref)} + onChange={(e) => { + const reader = new FileReader(); + reader.onload = (event) => { + setLandscapeCroppedData(event.target.result); + }; + reader.readAsDataURL(e.target.files[0]); + }} + /> + {landScapeImageInfo} +
+ {imageUploadModalViewLandscape} +
+
+
+ +
+
+ +
+
+
+
+ ); +}; + +export default PartnerHeader; diff --git a/frontend/src/components/PartnerSettings.jsx b/frontend/src/components/PartnerSettings.jsx new file mode 100644 index 00000000..a979c7d7 --- /dev/null +++ b/frontend/src/components/PartnerSettings.jsx @@ -0,0 +1,391 @@ +import React, { useEffect, useState, useContext } from 'react'; +import { toast } from "react-toastify"; +import { Context } from '../context/ContextApi.jsx'; +import { getTheme } from '../theme.jsx'; +import { + Button, + Typography, + Box, + IconButton, + Tooltip, +} from "@mui/material"; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { useNavigate } from 'react-router'; +import PartnerHeader from '../components/PartnerHeader.jsx'; +import PartnerDetails from '../components/PartnerDetails.jsx'; + +const PartnerSettings = (props) => { + const { + isCloud, + userdata, + globalUrl, + serverside, + loadingPartnerData, + selectedOrganization, + setSelectedOrganization, + handleGetOrg, + partnerData, + setPartnerData, + } = props; + + const [isPartner, setIsPartner] = React.useState(isCloud ? userdata?.active_org?.is_partner ? true : false : false); + const [partnerTypes, setPartnerTypes] = React.useState({}); + const [isPublishing, setIsPublishing] = React.useState(false); + const [isToggling, setIsToggling] = React.useState(false); + + useEffect(() => { + setIsPartner(isCloud ? userdata?.active_org?.is_partner ? true : false : false); + if(userdata?.support){ + setIsPartner(true); + } + }, [userdata, isCloud]); + // Partner Types handling : Getting from org status + useEffect(() => { + const partnerTypes = {}; + userdata?.org_status.forEach(status => { + if (status.includes("_partner")) { + partnerTypes[status] = true; + } + }); + setPartnerTypes(partnerTypes); + }, [selectedOrganization]); + // Partner Type Colors + const partnerTypeColors = { + "tech_partner": "#ff8544", + "distribution_partner": "#2BC07E", + "service_partner": "#a99cf9", + "integration_partner": "#fb47a0" + } + + const handleSendUpdateRequest = () => { + toast("Your request has been sent to the support team. They will review your request and get back to you as soon as possible.") + } + + // Open partner page in new tab + const handleOpenPartnerPage = () => { + if (partnerData?.id) { + if(partnerData?.public){ + window.open(`${window.location.origin}/partners/${partnerData.name.toLowerCase().replaceAll(" ", "_")}`) + }else{ + window.open(`${window.location.origin}/partners/${partnerData?.id}`, "_blank"); + } + } else { + toast.error("Partner ID not found"); + } + } + + // Update Partner Details + const handleUpdatePartnerDetails = () => { + // Validate required fields before publishing + const requiredStringFields = [ + { field: partnerData?.name, name: "Partner Name" }, + { field: partnerData?.description, name: "Description" }, + { field: selectedOrganization?.id, name: "Organization Id" }, + { field: partnerData?.image_url, name: "Logo Image" }, + { field: partnerData?.landscape_image_url, name: "Landscape Image" }, + { field: partnerData?.website_url, name: "Website URL" }, + { field: partnerData?.article_url, name: "Article URL" }, + { field: partnerData?.country, name: "Country" }, + { field: partnerData?.region, name: "Region" }, + ]; + + // Required array fields (multi-select dropdowns) + const requiredArrayFields = [ + { field: partnerData?.solutions, name: "Solutions" }, + ]; + + // Check if any required string fields are empty + const emptyStringFields = requiredStringFields.filter(item => + !item.field || item.field.trim() === "" + ); + + // Check if any required array fields are empty + const emptyArrayFields = requiredArrayFields.filter(item => + !item.field || !Array.isArray(item.field) || item.field.length === 0 + ); + + // Combine all empty fields + const allEmptyFields = [...emptyStringFields, ...emptyArrayFields]; + + // If there are empty required fields, show error and return + if (allEmptyFields.length > 0) { + const missingFields = allEmptyFields.map(item => item.name).join(", "); + toast.error(`Please fill in all required fields: ${missingFields}`); + return; + } + + // Check if at least one partner type is selected + if (Object.keys(partnerTypes).length === 0) { + toast.error("There should be at least one partner type"); + return; + } + + setIsPublishing(true); + const url = globalUrl + "/api/v1/partners/" + userdata?.active_org?.id; + const data = { + id: partnerData.id?.trim() || null, + name: partnerData.name?.trim(), + org_id: selectedOrganization?.id?.trim(), + description: partnerData.description?.trim(), + website_url: partnerData.website_url?.trim(), + article_url: partnerData.article_url?.trim(), + partner_type: partnerTypes, + expertise: partnerData?.expertise || [], + services: partnerData?.services || [], + solutions: partnerData?.solutions || [], + country: partnerData?.country || "", + region: partnerData?.region || "", + image_url: partnerData?.image_url?.trim(), + landscape_image_url: partnerData?.landscape_image_url?.trim(), + public: partnerData?.public + } + fetch(url, { + mode: "cors", + method: "POST", + body: JSON.stringify(data), + credentials: "include", + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => { + setIsPublishing(false); + if (response.status !== 200) { + toast.error("Failed to publish partner"); + } + return response.json(); + }) + .then((responseJson) => { + toast.success("Partner details successfully updated"); + }) + .catch((error) => { + setIsPublishing(false); + toast.error("Failed to update partner details: " + error?.message); + }) + } + + // Toggle Partner Publish Status + const handleTogglePublishStatus = () => { + setIsToggling(true); + const newPublishStatus = !partnerData?.public; + const url = globalUrl + "/api/v1/partners/" + userdata?.active_org?.id; + + const data = { + id: partnerData.id?.trim() || null, + name: partnerData.name?.trim(), + org_id: selectedOrganization?.id?.trim(), + description: partnerData.description?.trim(), + website_url: partnerData.website_url?.trim(), + article_url: partnerData.article_url?.trim(), + partner_type: partnerTypes, + usecases: partnerData?.usecases || [], + expertise: partnerData?.expertise || [], + services: partnerData?.services || [], + solutions: partnerData?.solutions || [], + country: partnerData?.country || "", + region: partnerData?.region || "", + image_url: partnerData?.image_url?.trim(), + landscape_image_url: partnerData?.landscape_image_url?.trim(), + public: newPublishStatus + } + + fetch(url, { + mode: "cors", + method: "POST", + body: JSON.stringify(data), + credentials: "include", + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then((response) => { + setIsToggling(false); + if (response.status !== 200) { + toast.error("Failed to update publish status"); + } + return response.json(); + }) + .then((responseJson) => { + // Update local state + setPartnerData(prev => ({ + ...prev, + public: newPublishStatus + })); + toast.success(`Partner ${newPublishStatus ? 'published' : 'unpublished'} successfully`); + }) + .catch((error) => { + setIsToggling(false); + toast.error("Failed to update publish status: " + error?.message); + }) + } + + const { themeMode } = useContext(Context); + const theme = getTheme(themeMode); + const navigate = useNavigate(); + + return ( +
+
+
+
+
+ + Configuration + {Object?.entries(partnerTypes)?.map(([key, value]) => ( + + {key.replace("_", " ").replace(/\b\w/g, char => char.toUpperCase())} + + ))} + + + + Set up and manage partner details and information to be displayed on the Partners page. + + + {isPartner && ( + <> + {/* View Partner Page Button */} + + + + + + + {/* Update Details Button */} + + {/* Toggle Publish/Unpublish Button */} + + + )} + + {!isPartner && ( + + )} + + +
+
+
+ + +
+
+ ) +} + +export default PartnerSettings; diff --git a/frontend/src/components/PartnerTab.jsx b/frontend/src/components/PartnerTab.jsx new file mode 100644 index 00000000..10c21f06 --- /dev/null +++ b/frontend/src/components/PartnerTab.jsx @@ -0,0 +1,245 @@ +import React, { useEffect, useState, useCallback, useContext } from 'react'; +import { useNavigate, useLocation } from "react-router-dom"; +import Branding from "../components/Branding.jsx"; +import { ToastContainer, toast } from "react-toastify"; +import { Button } from '@mui/material'; +import { getTheme } from '../theme.jsx'; +import { Context } from '../context/ContextApi.jsx'; +import PartnerSettings from '../components/PartnerSettings.jsx'; +import PartnersUsecasesTab from '../components/PartnersUsecasesTab.jsx'; +import PartnersApps from '../components/PartnerApps.jsx'; +import PartnerArticles from '../components/PartnersArticles.jsx'; +const PartnerTab = (props) => { + const location = useLocation(); + const navigate = useNavigate(); + const { + userdata, + globalUrl, + serverside, + isCloud, + setSelectedOrganization, + selectedOrganization, handleGetOrg, + handleStatusChange, + isLoaded, + removeCookie + } = props; + + + const [selectedTab, setSelectedTab] = useState('partner_settings'); + const [loadingPartnerData, setLoadingPartnerData] = useState(false); + const [curIndex, setCurIndex] = React.useState(0); + const [partnerData, setPartnerData] = React.useState({ + name: "", + description: "", + image_url: "", + landscape_image_url: "", + website_url: "", + article_url: "", + expertise: [], + usecases: [], + services: [], + solutions: [], + country: "", + region: "", + partner_type: {}, + }); + + const tabsOnPartnerTab = [ 'Partner Settings', 'Usecases', 'Apps', 'AI Agents', 'Articles', 'Branding']; + + const { themeMode } = useContext(Context); + const theme = getTheme(themeMode); + + useEffect(() => { + if(!isCloud || !userdata?.active_org?.is_partner) { + // If the user is not a partner or if it's not a cloud environment do not make api call :) + return; + } + setLoadingPartnerData(true); + const url = globalUrl + "/api/v1/partners/" + userdata?.active_org?.id; + fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + toast("Failed to get partner data") + } + return response.json(); + }) + .then((responseJson) => { + if(responseJson.success) { + setPartnerData(responseJson?.partner); + console.log("responseJson", responseJson) + setLoadingPartnerData(false); + }else{ + setLoadingPartnerData(false); + toast(responseJson?.reason) + } + }) + .catch((error) => { + setLoadingPartnerData(false); + }) + }, [globalUrl]); + + // Used to auto select the tab based on the url : partner_tab + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const tabName = queryParams.get('partner_tab'); + if (tabName) { + const decodedTabName = decodeURIComponent(tabName); + setSelectedTab(decodedTabName); + if (decodedTabName === 'partner_settings') { + setCurIndex(0); + } else if(decodedTabName === 'usecases'){ + setCurIndex(1) + }else if (decodedTabName === 'apps'){ + setCurIndex(2); + } else if (decodedTabName === 'aiagents') { + setCurIndex(3); + } else if (decodedTabName === 'articles') { + setCurIndex(4); + } else if (decodedTabName === 'branding') { + setCurIndex(5); + } + } + }, [location.search]); + + // Tab click on partner tab + const handleTabClick = (tabName) => { + const formattedTabName = tabName.toLowerCase().replace(/[\s&]+/g, ''); + const encodedTabName = encodeURIComponent(formattedTabName); + setSelectedTab(formattedTabName); + document.title = `Shuffle - partner - ${formattedTabName}`; + navigate(`?partner_tab=${encodedTabName}`); + }; + + // Rendering the content based on the selected tab + const renderContent = () => { + switch (selectedTab) { + case 'partner_settings': + return ; + case 'usecases': + return ; + case `apps`: + return ; + case 'articles' : + return ; + case `ai_agents`: + return ; + case 'branding': + return ; + default: + return ; + } + }; + + const isTabDisabled = (tabName) => { + // If user is a support user, enable all tabs + if (userdata?.support) { + return false; + } + + // For non-support users, apply the following restrictions: + + // For onPrem only partner settings and branding are enabled + if ( + !isCloud && + (tabName === "Usecases" || + tabName === "Apps" || + tabName === "AI Agents" || + tabName === "Articles") + ) { + return true; + } + + // Disable Apps, Articles, and AI Agents for all non-support users + if ( + tabName === "Apps" || + tabName === "Articles" || + tabName === "AI Agents" + ) { + return true; + } + + // Disable Usecases tab for cloud users if not a partner or is in a sub-org + if (isCloud && tabName === "Usecases") { + if ( + !userdata?.active_org?.is_partner || + userdata?.active_org?.is_sub_org + ) { + return true; + } + } + + // Disable Branding tab if not an integration partner + const isIntegrationPartner = + userdata && + userdata?.org_status?.includes("integration_partner") && + !userdata?.org_status?.includes("sub_org"); + if (tabName === "Branding" && !isIntegrationPartner) { + return true; + } + + // Enable the tab by default + return false; + } + + return ( +
+
+ {tabsOnPartnerTab?.map((tabName, index) => ( +
+ +
+ ))} +
+
+ {renderContent()} +
+
+ ); +}; + +export default PartnerTab; diff --git a/frontend/src/components/PartnersArticles.jsx b/frontend/src/components/PartnersArticles.jsx new file mode 100644 index 00000000..80653231 --- /dev/null +++ b/frontend/src/components/PartnersArticles.jsx @@ -0,0 +1,21 @@ +import { Box, Typography } from '@mui/material' +import React from 'react' + +const PartnersArticles = () => { + return ( + + Partner Articles + + ) +} + +export default PartnersArticles diff --git a/frontend/src/components/PartnersUsecasesTab.jsx b/frontend/src/components/PartnersUsecasesTab.jsx new file mode 100644 index 00000000..87e34b52 --- /dev/null +++ b/frontend/src/components/PartnersUsecasesTab.jsx @@ -0,0 +1,1761 @@ +import { + Box, + Tooltip, + Typography, + Switch, + Button, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Select, + MenuItem, + FormControl, + InputLabel, + IconButton, + Menu, + ListItemIcon, + ListItemText, + Checkbox, + CircularProgress, + Skeleton, +} from "@mui/material"; +import React, { useContext, useEffect, useState } from "react"; +import { getTheme } from "../theme.jsx"; +import { Context } from "../context/ContextApi.jsx"; +import AddIcon from "@mui/icons-material/Add"; +import CloseIcon from "@mui/icons-material/Close"; +import { toast } from "react-toastify"; +import MoreVertIcon from "@mui/icons-material/MoreVert"; +import EditIcon from "@mui/icons-material/Edit"; +import StarIcon from "@mui/icons-material/Star"; +import DeleteIcon from "@mui/icons-material/Delete"; +import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; +import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; +import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import { Link, useNavigate } from "react-router-dom"; + +// Helper function to get the correct image path based on app category +export const getCategoryImagePath = (category) => { + if (!category) return "/images/appCategories/other.svg"; + + const lowerCategory = category.toLowerCase(); + + if (lowerCategory.includes("communication")) { + return "/images/appCategories/cases.svg"; + } else if (lowerCategory.includes("cases")) { + return "/images/appCategories/cases.svg"; + } else if (lowerCategory.includes("network")) { + return "/images/appCategories/network.svg"; + } else if (lowerCategory.includes("siem")) { + return "/images/appCategories/siem.svg"; + } else if (lowerCategory.includes("edr") || lowerCategory.includes("eradication")) { + return "/images/appCategories/edr.svg"; + } else if (lowerCategory.includes("iam")) { + return "/images/appCategories/iam.svg"; + } else if (lowerCategory.includes("assets")) { + return "/images/appCategories/assets.svg"; + } else if (lowerCategory.includes("intel")) { + return "/images/appCategories/intel.svg"; + } else if (lowerCategory.includes("email")) { + return "/images/appCategories/email.svg"; + } else { + return "/images/appCategories/other.svg"; + } +}; + +// Skeleton component for loading state +const UsecaseCardSkeleton = () => { + const { themeMode } = useContext(Context); + const theme = getTheme(themeMode); + + return ( + + +
+ {/* Source App Icon Skeleton */} + + + {/* Destination App Icon Skeleton */} + +
+
+ + +
+
+ +
+ ); +}; + +const UsecaseCard = ({ usecase, handleToggle, handleOpenDialog, handleDeleteUsecase }) => { + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + const { themeMode } = useContext(Context); + const theme = getTheme(themeMode); + const navigate = useNavigate(); + + const handleClick = (event) => { + event.stopPropagation(); + event.preventDefault(); + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const handleEdit = () => { + console.log("Edit usecase:", usecase.id); + handleClose(); + }; + + const handleStar = () => { + console.log("Star usecase:", usecase.id); + handleClose(); + }; + + const handleCardClick = (e) => { + // Navigate to usecase detail page + navigate(`/usecases/${usecase.id}`); + }; + + return ( + + +
+ {/* Source App Icon */} + + Source + + + {/* Destination App Icon */} + + Destination + +
+
+ { + e.stopPropagation(); + }} + onChange={(e) => { + e.preventDefault(); + e.stopPropagation(); + handleToggle(usecase.id); + }} + size="medium" + sx={{ + "& .MuiSwitch-switchBase.Mui-checked": { + color: "#4CAF50", + }, + "& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track": { + backgroundColor: "#4CAF50", + }, + }} + /> + { + e.stopPropagation(); + handleClick(e); + }} + size="small" + sx={{ + color: theme.palette.text.primary, + zIndex: 20, + "&:hover": { + backgroundColor: themeMode === "dark" ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.05)", + }, + }} + > + + + e.stopPropagation()} + PaperProps={{ + sx: { + backgroundColor: theme.palette.DialogStyle.backgroundColor, + border: theme.palette.defaultBorder, + borderRadius: "8px", + boxShadow: theme.palette.DialogStyle.boxShadow, + "& .MuiMenuItem-root": { + fontSize: "14px", + color: theme.palette.text.primary, + "&:hover": { + backgroundColor: themeMode === "dark" ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.05)", + }, + }, + }, + }} + transformOrigin={{ horizontal: "right", vertical: "top" }} + anchorOrigin={{ horizontal: "right", vertical: "bottom" }} + > + { + e.stopPropagation(); + handleOpenDialog(usecase); + setAnchorEl(null); + }} + > + + + + Edit Usecase + + { + e.stopPropagation(); + handleDeleteUsecase(usecase.id); + }}> + + + + Delete Usecase + + +
+
+ + {usecase.name} + +
+ ); +}; + +const PartnersUsecasesTab = ({ isCloud, globalUrl, userdata, partnerData, setPartnerData }) => { + const { themeMode } = useContext(Context); + const theme = getTheme(themeMode); + + // Dialog state + const [openDialog, setOpenDialog] = useState(false); + const [publicWorkflows, setPublicWorkflows] = useState([]); + const [usecaseData, setUsecaseData] = useState([]); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [isWorkflowLoading, setIsWorkflowLoading] = useState(false); + const [formData, setFormData] = useState({ + id: "", + mainContent: { + title: "", + description: "", + categories: [], + publicWorkflowId: "", + sourceAppType: "", + destinationAppType: "", + }, + navigation: { + items: [ + { + name: "About Usecase", + content: [""], + }, + ], + }, + public: false, + }); + + // App categories for dropdowns + const appCategories = [ + { value: "communication", label: "Communication" }, + { value: "cases", label: "Cases" }, + { value: "network", label: "Network" }, + { value: "siem", label: "SIEM" }, + { value: "edr", label: "EDR" }, + { value: "eradication", label: "Eradication" }, + { value: "iam", label: "IAM" }, + { value: "assets", label: "Assets" }, + { value: "intel", label: "Intel" }, + { value: "email", label: "Email" }, + { value: "other", label: "Other" } + ]; + + // Sample workflow options + const workflowOptions = [ + "Email Analysis Workflow", + "Incident Response Workflow", + "Alert Triage Workflow", + "Threat Hunting Workflow", + "Vulnerability Management", + ]; + + useEffect(() => { + // Set loading state to true when fetching starts + setIsLoading(true); + if(!isCloud || !userdata?.active_org?.is_partner) { + // If the user is not a partner or if it's not a cloud environment do not make api call :) + return; + } + // Load usecase data from API + fetch(`${globalUrl}/api/v1/partners/${partnerData?.id}/usecases`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for PARTNER USECASES :O!"); + } + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success !== false) { + const usecases = responseJson.usecases.map((usecase) => ({ + id: usecase.id, + sourceAppType: usecase.mainContent?.sourceAppType || "", + destinationAppType: usecase.mainContent?.destinationAppType || "", + srcImg: getCategoryImagePath(usecase.mainContent?.sourceAppType), + dstImg: getCategoryImagePath(usecase.mainContent?.destinationAppType), + publicWorkflowId: usecase.mainContent?.publicWorkflowId || "", + name: usecase.mainContent?.title, + description: usecase.mainContent?.description, + navigation: usecase.navigation || { items: [] }, + categories: usecase.mainContent?.categories || [], + public: usecase?.public || false, + })); + if (usecases.length > 0) { + setUsecaseData(usecases); + } else { + setUsecaseData([]); + } + } + }) + .catch((error) => { + console.log(error); + }) + .finally(() => { + // Set loading state to false when fetching completes (success or error) + setIsLoading(false); + }); + }, [partnerData?.id]); + + // Sample categories + const categoryOptions = ["Collect","Enrich", "Detect", "Respond", "Verify"]; + + const getUserProfileWorkflows = (orgId) => { + setIsWorkflowLoading(true); + fetch(`${globalUrl}/api/v1/partners/${orgId}/workflows`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for WORKFLOW EXECUTION :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success !== false) { + setPublicWorkflows(responseJson || []); + setIsWorkflowLoading(false); + }else { + toast.info(responseJson.message || "No public workflows found for this organization."); + setPublicWorkflows([]); + } + }) + .catch((error) => { + console.log(error); + setIsWorkflowLoading(false); + }); + } + + useEffect(() => { + const orgId = userdata?.active_org?.id; + if(!isCloud || !userdata?.active_org?.is_partner) { + // If the user is not a partner or if it's not a cloud environment do not make api call :) + return; + } + getUserProfileWorkflows(orgId); + }, []); + + + const handleUpdateUsecase = async (usecaseId, updatedData) => { + try { + // Transform the frontend data structure to match the backend expected structure + const backendUsecaseData = { + id: updatedData.id, + companyInfo: { + id: partnerData?.id.trim(), + name: partnerData?.name.trim(), + }, + mainContent: { + title: updatedData.name.trim(), + description: updatedData.description.trim(), + categories: updatedData.categories, + publicWorkflowId: updatedData.publicWorkflowId.trim(), + sourceAppType: updatedData.sourceAppType.trim(), + destinationAppType: updatedData.destinationAppType.trim(), + }, + navigation: updatedData.navigation, + public: updatedData.public, + edited: Date.now(), + }; + + + // return + const response = await fetch(`${globalUrl}/api/v1/usecases/${usecaseId}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(backendUsecaseData), + }); + + if (response.status !== 200) { + console.error("Failed to update usecase"); + toast.error("Failed to update usecase"); + return false; + } + + const responseData = await response.json(); + toast.success("Usecase published status updated successfully"); + return true; + } catch (error) { + console.error("Error updating usecase:", error); + toast.error("Error updating usecase"); + return false; + } + }; + + const handleDeleteUsecase = async (usecaseId) => { + try { + const response = await fetch( + `${globalUrl}/api/v1/usecases/${usecaseId}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + } + ); + if (response.status !== 200) { + console.error("Failed to delete usecase"); + toast.error("Failed to delete usecase"); + return false; + } + const responseData = await response.json(); + toast.success("Usecase deleted successfully"); + // Remove the deleted usecase from the state + setUsecaseData((prevData) => + prevData?.filter((usecase) => usecase.id !== usecaseId) + ); + // Remove the usecase ID from partnerData + setPartnerData((prevData) => ({ + ...prevData, + usecases: prevData.usecases?.filter((id) => id !== usecaseId), + })); + return true; + } catch (error) { + console.error("Error deleting usecase:", error); + toast.error("Error deleting usecase"); + return false; + } + }; + + const handleToggle = async (currentId) => { + // Find the current usecase + const currentUsecase = usecaseData.find(usecase => usecase.id === currentId); + if (!currentUsecase) return; + + // Optimistically update the UI + setUsecaseData((prevData) => + prevData.map((usecase) => + usecase.id === currentId + ? { ...usecase, public: !usecase.public } + : usecase + ) + ); + + // Prepare the updated data + const updatedPublishedStatus = !currentUsecase.public; + + // Create a copy of the usecase with updated published status + const updatedUsecase = { + ...currentUsecase, + public: updatedPublishedStatus + }; + toast.info(`Updating usecase ${updatedUsecase.name}...`,{ + autoClose: 1000, + }) + // Send the update to the server + const success = await handleUpdateUsecase(currentId, updatedUsecase); + + // If the update failed, revert the UI change + if (!success) { + setUsecaseData((prevData) => + prevData.map((usecase) => + usecase.id === currentId + ? { ...usecase, public: currentUsecase.public } + : usecase + ) + ); + } + }; + + const handleOpenDialog = (usecase) => { + setFormData({ + id: usecase?.id || undefined, + companyInfo: { + id: usecase.companyInfo?.id || "", + name: usecase.companyInfo?.name || "", + }, + mainContent: { + title: usecase?.name, + description: usecase?.description, + categories: usecase?.categories || [], + publicWorkflowId: usecase?.publicWorkflowId || "", + sourceAppType: usecase?.sourceAppType || "", + destinationAppType: usecase?.destinationAppType || "", + }, + navigation: usecase?.navigation || { + items: [ + { + name: "About Usecase", + content: [""], + }, + ], + }, + public: usecase?.public || false, + }); + setOpenDialog(true); + }; + + const handleCloseDialog = () => { + setOpenDialog(false); + setIsSubmitting(false); // Reset loading state when closing dialog + setFormData({ + id: "", + mainContent: { + title: "", + description: "", + categories: [], + publicWorkflowId: "", + sourceAppType: "", + destinationAppType: "", + }, + navigation: { + items: [ + { + name: "About Usecase", + content: [""], + }, + ], + }, + public: false, + }); + }; + + const handleSubmit = () => { + // Validate required fields + const validationErrors = []; + + // Check for public workflow selection + if (!formData.mainContent.publicWorkflowId || formData.mainContent.publicWorkflowId.trim() === "") { + validationErrors.push("Please select a public workflow"); + } + + if (!formData.mainContent.sourceAppType || formData.mainContent.sourceAppType.trim() === "") { + validationErrors.push("Source app type is required"); + } + + if (!formData.mainContent.destinationAppType || formData.mainContent.destinationAppType.trim() === "") { + validationErrors.push("Destination app type is required"); + } + + // Validate that source and destination are different + if (formData.mainContent.sourceAppType && formData.mainContent.destinationAppType && + formData.mainContent.sourceAppType.trim() === formData.mainContent.destinationAppType.trim()) { + validationErrors.push("Source and destination app types must be different"); + } + + // Check main content fields + if (!formData.mainContent.title || formData.mainContent.title.trim() === "") { + validationErrors.push("Title is required"); + } else if (formData.mainContent.title.length < 5) { + validationErrors.push("Title must be not be less than 5 characters"); + } + + if (!formData.mainContent.description || formData.mainContent.description.trim() === "") { + validationErrors.push("Description is required"); + } + + // Check categories + if (!formData.mainContent.categories || !Array.isArray(formData.mainContent.categories) || formData.mainContent.categories.length === 0) { + validationErrors.push("At least one category must be selected"); + } + + // Check navigation items + if (!formData.navigation.items || !Array.isArray(formData.navigation.items) || formData.navigation.items.length === 0) { + validationErrors.push("Please add at least one section"); + } else { + // Validate each section + for (let i = 0; i < formData.navigation.items.length; i++) { + const item = formData.navigation.items[i]; + + if (!item.name || item.name.trim() === "") { + validationErrors.push(`Section ${i+1} must have a name`); + } + + if (!item.content || !Array.isArray(item.content) || item.content.length === 0 || + !item.content.some(content => content && content.trim() !== "")) { + validationErrors.push(`Section "${item.name || i+1}" must have content`); + } + } + } + + // If there are validation errors, show the first one and return + if (validationErrors.length > 0) { + toast.error(validationErrors[0]); + return; + } + + // Set loading state to true + setIsSubmitting(true); + + // Get the image paths for source and destination app types + const srcImg = getCategoryImagePath(formData.mainContent.sourceAppType); + const dstImg = getCategoryImagePath(formData.mainContent.destinationAppType); + + const response = { + ...formData, + companyInfo: { + id: partnerData?.id, + name: partnerData?.name, + }, + } + + fetch(`${globalUrl}/api/v1/usecases/${formData.id}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(response), + }) + .then((res) => { + if (res.status !== 200) { + console.error("Failed to submit usecase"); + toast.error("Failed to submit usecase"); + setIsSubmitting(false); + return null; + } + return res.json(); + }) + .then((responseData) => { + console.log("Response data:", responseData); + // Reset loading state + setIsSubmitting(false); + + if (responseData) { + // Only close the dialog if the request was successful + const usecaseData = { + id: responseData.usecaseId.trim(), + sourceAppType: formData.mainContent?.sourceAppType.trim() || "", + destinationAppType: formData.mainContent?.destinationAppType.trim() || "", + srcImg: getCategoryImagePath(formData.mainContent?.sourceAppType.trim()), + dstImg: getCategoryImagePath(formData.mainContent?.destinationAppType.trim()), + publicWorkflowId: formData.mainContent?.publicWorkflowId.trim() || "", + name: formData.mainContent?.title.trim(), + description: formData.mainContent?.description.trim(), + categories: formData.mainContent?.categories || [], + navigation: formData.navigation || {}, + public: formData?.public || false, + } + + setUsecaseData((prevData) => { + // Handle case when prevData is null or undefined + if (!prevData) { + return [usecaseData]; + } + + // Check if the usecase already exists + const existingIndex = prevData.findIndex( + (usecase) => usecase.id === responseData.usecaseId + ); + if (existingIndex !== -1) { + // Update existing usecase + const updatedData = [...prevData]; + updatedData[existingIndex] = usecaseData; + return updatedData; + } else { + // Add new usecase + return [...prevData, usecaseData]; + } + }); + + setPartnerData((prevData) => { + // Handle case when prevData is null or undefined + if (!prevData) { + return { + usecases: [responseData.usecaseId.trim()] + }; + } + + return { + ...prevData, + usecases: [ + ...(prevData.usecases || []), + responseData.usecaseId.trim(), + ], + }; + }); + + // Show success message + if(formData?.id){ + toast.success("Usecase updated successfully"); + }else{ + toast.success("Usecase created successfully, publish it with toggle button"); + } + handleCloseDialog(); + } + }) + .catch((error) => { + console.error("Error submitting usecase:", error); + toast.error("Error submitting usecase"); + // Reset loading state on error + setIsSubmitting(false); + }); + }; + + return ( + + {/* Add Usecase Button */} + + + Usecases + + + + + {/* Usecases Grid */} + {isLoading ? ( + // Skeleton loading state + + {/* Display 4 skeleton cards while loading */} + {[...Array(4)].map((_, index) => ( + + ))} + + ) : usecaseData.length > 0 ? ( + // Actual data display + + {usecaseData.map((usecase) => ( + + ))} + + ) : ( + // Empty state + + + No usecases found + + + )} + + {/* Add Usecase Dialog */} + + + + {formData?.id ? "Update" : "Add New"} Usecase : {formData?.public ? "Published" : "Draft"} + + + + + + + + + + + Public Workflow + + + + + {/* App Type Selection */} + + + + Source App Type + + + + + + + Destination App Type + + + + + + + + Main Content + + + + + Title + + + setFormData({ + ...formData, + mainContent: { + ...formData.mainContent, + title: e.target.value, + }, + }) + } + sx={{ + "& .MuiOutlinedInput-root": { + height: 40, + backgroundColor: theme.palette.usecaseDialogFieldColor, + color: theme.palette.text.primary, + "& fieldset": { border: theme.palette.defaultBorder }, + }, + }} + /> + + + + Description + + + setFormData({ + ...formData, + mainContent: { + ...formData.mainContent, + description: e.target.value, + }, + }) + } + sx={{ + "& .MuiOutlinedInput-root": { + backgroundColor: theme.palette.usecaseDialogFieldColor, + p: 1.5, + color: theme.palette.text.primary, + "& fieldset": { border: theme.palette.defaultBorder }, + }, + }} + /> + + + + Categories + + + + + + + {/* Navigation Items Section */} + + + Navigation Items + + {formData.navigation.items.map((item, itemIndex) => ( + + + { + const newItems = [...formData.navigation.items]; + newItems[itemIndex].name = e.target.value; + setFormData({ + ...formData, + navigation: { items: newItems }, + }); + }} + placeholder="Section Name" + sx={{ + flex: 1, + "& .MuiOutlinedInput-root": { + height: 40, + backgroundColor: theme.palette.usecaseDialogFieldColor, + color: theme.palette.text.primary, + "& fieldset": { border: theme.palette.defaultBorder }, + }, + }} + /> + + { + if (itemIndex > 0) { + const newItems = [...formData.navigation.items]; + [newItems[itemIndex - 1], newItems[itemIndex]] = [ + newItems[itemIndex], + newItems[itemIndex - 1], + ]; + setFormData({ + ...formData, + navigation: { items: newItems }, + }); + } + }} + disabled={itemIndex === 0} + sx={{ + color: theme.palette.text.secondary, + "&:not(:disabled):hover": { + backgroundColor: themeMode === "dark" ? "rgba(255, 255, 255, 0.08)" : "rgba(0, 0, 0, 0.05)", + }, + }} + > + + + { + if ( + itemIndex < + formData.navigation.items.length - 1 + ) { + const newItems = [...formData.navigation.items]; + [newItems[itemIndex], newItems[itemIndex + 1]] = [ + newItems[itemIndex + 1], + newItems[itemIndex], + ]; + setFormData({ + ...formData, + navigation: { items: newItems }, + }); + } + }} + disabled={ + itemIndex === formData.navigation.items.length - 1 + } + sx={{ + color: theme.palette.text.secondary, + "&:not(:disabled):hover": { + backgroundColor: themeMode === "dark" ? "rgba(255, 255, 255, 0.08)" : "rgba(0, 0, 0, 0.05)", + }, + }} + > + + + { + const newItems = formData.navigation.items?.filter( + (_, i) => i !== itemIndex + ); + setFormData({ + ...formData, + navigation: { items: newItems }, + }); + }} + sx={{ + color: "#ff4444", + "&:hover": { + backgroundColor: themeMode === "dark" ? "rgba(255, 68, 68, 0.08)" : "rgba(255, 68, 68, 0.15)", + }, + }} + > + + + + + + {item.content.map((content, contentIndex) => ( + + { + const newItems = [...formData.navigation.items]; + newItems[itemIndex].content[contentIndex] = + e.target.value; + setFormData({ + ...formData, + navigation: { items: newItems }, + }); + }} + placeholder="Content" + fullWidth + sx={{ + "& .MuiOutlinedInput-root": { + backgroundColor: theme.palette.usecaseDialogFieldColor, + color: theme.palette.text.primary, + "& fieldset": { border: theme.palette.defaultBorder }, + }, + }} + /> + {item.content.length > 1 && ( + + )} + + ))} + + + + ))} + + + + + + + + + + + + + ); +}; + +export default PartnersUsecasesTab; diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index cd9d5e0d..639c6263 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -89,49 +89,18 @@ const Priorities = memo((props) => { useEffect(() => { prepareNotificationAppList() + + if (notifications === undefined || notifications === null || notifications.length === 0) { + getNotifications() + } }, []); let navigate = useNavigate(); const classes = useStyles(); - const getAppIDs = async (appList) => { - fetch(globalUrl + "/api/v1/apps", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }).then((response) => { - if (response.status !== 200) { - toast("Failed getting app ids: ", response.reason); - console.log("Status not 200 for app ids :O!"); - return; - } - return response.json(); - }).then((responseJson) => { - if (responseJson !== undefined) { - // console.log("App ids: ", responseJson) - // console.log("App list: ", appList) - const filteredApps = responseJson.filter(app => appList.includes(app.name)); - const appDetails = filteredApps.map(app => ({ name: app.name, id: app.id })); - return appDetails - // console.log("App IDs: ", appDetails) - } - }).catch((error) => { - console.log("Error getting app ids: " + error); - }) - } // getting comms & cases app from app framework var notificationAppList = []; - // if (selectedOrganization.security_framework?.cases && selectedOrganization.security_framework?.cases.name.length > 0) { - // notificationAppList = notificationAppList.concat(selectedOrganization.security_framework?.cases); - // } - // if (selectedOrganization.security_framework?.communication && selectedOrganization.security_framework?.communication.name.length > 0) { - // notificationAppList = notificationAppList.concat(selectedOrganization.security_framework?.communication); - // } - const mergeAuthData = (result, responseJson) => { // result is only getting: Jira AND discord. @@ -146,7 +115,7 @@ const Priorities = memo((props) => { return updatedResult; }; - const prepareNotificationAppList = async () => { + const prepareNotificationAppList = () => { // getting App ID,Authentication fields and saved auths for each app var result = [] fetch(globalUrl + "/api/v1/apps", { @@ -187,18 +156,17 @@ const Priorities = memo((props) => { return; } return response.json(); - }).then(async (responseJson) => { + }).then((responseJson) => { if (!responseJson.success) { console.log("Could not get app auth") return; } // console.log("responseJson of auth: ", responseJson.data) - result = await mergeAuthData(appDetails, responseJson.data) + result = mergeAuthData(appDetails, responseJson.data) // console.log("merged auth data: ", result) // console.log("result", result) result.map(item => { - console.log("Getting app config for: ", item.name); fetch(globalUrl + `/api/v1/apps/${item.id}/config`, { method: "GET", headers: { @@ -235,9 +203,9 @@ const Priorities = memo((props) => { setnotificationAppsDetails(newJSON); console.log("notificationAppsDetails: ", newJSON) } - }).then(async () => { + }).then(() => { // removing this for now, - // await checkIfAlreadyGenerated(filteredApps, workflows); + // checkIfAlreadyGenerated(filteredApps, workflows); }).catch((error) => { console.log("Error getting app config: " + error); @@ -280,143 +248,6 @@ const Priorities = memo((props) => { console.log("Apps: ", notificationAppsDetails); }, [notificationAppsDetails]) - // const generateJiraNotificationWorkflow = async (app, appAuthId, projectId, issuetype) => { - // //currently only supports JIRA figure out a way to support more apps - - // var appname = app.name; - // var appImage = app.large_image; - - // var workflowName = `[GENERATED] ${appname} notification workflow` - // var workflowDescription = "Generated by Shuffle for sending info/error notifications." - // var data = { - // "name": workflowName, - // "description": workflowDescription, - // } - - // fetch(globalUrl + "/api/v1/workflows", { - // method: "POST", - // headers: { - // "Content-Type": "application/json", - // Accept: "application/json", - // }, - // body: JSON.stringify(data), - // credentials: "include", - // }) - // .then((response) => { - // if (response.status !== 200) { - // toast("Failed setting notification workflow: ", response.reason); - // console.log("Status not 200 for workflows :O!"); - // return; - // } - // return response.json(); - // }).then((responseJson) => { - // if (responseJson !== undefined) { - // console.log("Notification workflow created successfully") - // var workflow_id = responseJson.id - // if (appname.toLowerCase() === "jira") { - // console.log("updating workflow for JIRA") - // var workflowBody = { - // "name": workflowName, - // "Description": workflowDescription, - // "id": workflow_id, - // "actions": [ - // { - // "app_id": app.id, - // "app_name": "jira", - // "name": "post_create_issue", - // "authentication_id": appAuthId, - // "large_image": appImage, - // "isStartNode": true, - // "label": "create_issue", - // "app_version": "1.1.0", - // "parameters": [ - // { - // "name": "body", - // "value": `{"fields": { "project": {"key": "${projectId}"},"summary": "$exec.title","issuetype": {"name": "${issuetype}"},"description": {"content": [{"content":[{"type":"text","text":"$exec.description"}],"type": "paragraph"}],"type": "doc","version": 1}}}` - // }, - // { - // "name": "username_basic", - // "value": "" - // }, - // { - // "name": "password_basic", - // "value": "" - // }, - // { - // "name": "url", - // "value": "" - // }, - // { - // "name": "headers", - // "value": "Content-type=application/json \nAccept=application/json" - // }, - // { - // "name": "queries", - // "value": "" - // }, - // { - // "name": "ssl_verify", - // "value": "False" - // } - // ] - // } - // ] - // } - // } - // fetch(globalUrl + `/api/v1/workflows/${workflow_id}`, { - // method: "PUT", - // headers: { - // "Content-Type": "application/json", - // Accept: "application/json", - // }, - // body: JSON.stringify(workflowBody), - // credentials: "include", - // }) - // .then((response) => { - // if (response.status !== 200) { - // toast("Failed setting notification workflow: ", response.reason); - // console.log("Status not 200 for workflows :O!"); - // return; - // } - // return response.json(); - // }).then((responseJson) => { - // if (responseJson !== undefined) { - // handleEditOrg( - // selectedOrganization.name, - // selectedOrganization.description, - // selectedOrganization.id, - // selectedOrganization.image, - // { - // documentation_reference: selectedOrganization?.defaults?.documentation_reference, - // workflow_upload_repo: selectedOrganization?.defaults?.workflow_upload_repo, - // workflow_upload_branch: selectedOrganization?.defaults?.workflow_upload_branch, - // workflow_upload_username: selectedOrganization?.defaults?.workflow_upload_username, - // workflow_upload_token: selectedOrganization?.defaults?.workflow_upload_token, - // newsletter: selectedOrganization?.defaults?.newsletter, - // weekly_recommendations: selectedOrganization?.defaults?.weekly_recommendations, - // notification_workflow: workflow_id, - // }, - // { - // sso_entrypoint: selectedOrganization?.sso_config?.sso_entrypoint, - // sso_certificate: selectedOrganization?.sso_config?.sso_certificate, - // client_id: selectedOrganization?.sso_config?.client_id, - // client_secret: selectedOrganization?.sso_config?.client_secret, - // openid_authorization: selectedOrganization?.sso_config?.openid_authorization, - // openid_token: selectedOrganization?.sso_config?.openid_token, - // SSORequired: selectedOrganization?.sso_config?.SSORequired, - // auto_provision: selectedOrganization?.sso_config?.auto_provision, - // } - // ) - // console.log("Notification workflow updated successfully") - // toast("Notification workflow updated successfully") - // } - // }) - // } - // }).catch((error) => { - // console.log("Error setting workflows: " + error); - // }) - // } - const generateCommsNotificationWorkflow = async (app, sender, recepient) => { var appname = app.name var appImage = app.large_image @@ -984,12 +815,13 @@ print('"' + encoded + '"')
- {`Configure ${selectedAppDetails.name} workflow`} + {`Configure ${selectedAppDetails?.name?.replaceAll("_", " ")} workflow`}
{console.log("len Selected app details: ", selectedAppDetails)} + {(selectedAppDetails.authentication_data || (selectedAppDetails.auth_config && selectedAppDetails.auth_config.required == false) || (selectedAppDetails.authentication && selectedAppDetails.authentication.required == false)) ? <> @@ -1152,13 +984,15 @@ print('"' + encoded + '"') const renderChips = useCallback(() => { const appList = Object.values(notificationAppsDetails); + return ( + {appList.map((app) => ( { @@ -1176,6 +1010,7 @@ print('"' + encoded + '"') avatar={{app.name}} /> ))} + Set up app authentication - to unlock additional workflow options. + to show additional workflow options. @@ -1283,10 +1118,38 @@ print('"' + encoded + '"') }) } + const getNotifications = () => { + fetch(`${globalUrl}/api/v1/notifications`, { + 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 !== false && responseJson?.notifications !== undefined && responseJson?.notifications !== null) { + setNotifications(responseJson.notifications || []) + } else { + toast("Failed loading notifications. Please try again later."); + } + }) + .catch((error) => { + console.log("error in notification loading: ", error); + }); + + } + const clearNotifications = () => { // Don't really care about the logout - toast("Clearing notifications") + toast.info("Marking all notifications as read. This may take a while.") fetch(`${globalUrl}/api/v1/notifications/clear`, { credentials: "include", method: "GET", diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index 3132034f..043c5de2 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -64,6 +64,7 @@ import AceEditor from "react-ace"; import ace from "ace-builds"; import 'ace-builds/src-noconflict/mode-python'; import 'ace-builds/src-noconflict/mode-json'; +import 'ace-builds/src-noconflict/mode-yaml'; //import 'ace-builds/src-noconflict/theme-twilight'; //import 'ace-builds/src-noconflict/theme-solarized_dark'; import 'ace-builds/src-noconflict/theme-gruvbox'; @@ -119,6 +120,7 @@ const CodeEditor = (props) => { handleActionParamChange, setcodedata, isFileEditor, + isWorkflowEditor, runUpdateText, toolsAppId, parameterName, @@ -143,11 +145,6 @@ const CodeEditor = (props) => { const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); - //const { setContainer } = useCodeMirror({ - // container: editorRef.current, - // extensions, - // value: localcodedata, - //}) // const {codelang, setcodelang} = props const {themeMode, supportEmail} = useContext(Context) const theme = getTheme(themeMode) @@ -1529,13 +1526,15 @@ const CodeEditor = (props) => { // zIndex: 12501, pointerEvents: "auto", color: theme.palette.DialogStyle.color, - minWidth: isMobile ? "100%" : isFileEditor ? "650px" : "80%", - maxWidth: isMobile ? "100%" : isFileEditor ? "650px" : "1100px", - minHeight: isMobile ? "100%" : "auto", - maxHeight: isMobile ? "100%" : "700px", + minWidth: isMobile || isWorkflowEditor ? "100%" : isFileEditor ? "650px" : "80%", + maxWidth: isMobile || isWorkflowEditor ? "100%" : isFileEditor ? "650px" : "1100px", + minHeight: isMobile || isWorkflowEditor ? "100%" : "auto", + maxHeight: isMobile || isWorkflowEditor ? "100%" : "700px", border: "3px solid rgba(255,255,255,0.3)", - padding: isMobile ? "25px 10px 25px 10px" : "25px", + padding: isMobile ? "25px 10px 25px 10px" : isWorkflowEditor ? "25px 10px 25px 200px" : "25px", backgroundColor: themeMode === "dark" ? "black" : theme.palette.DialogStyle.backgroundColor, + + opacity: isWorkflowEditor ? 0.93 : 1, }, }} > @@ -1719,7 +1718,7 @@ const CodeEditor = (props) => { Code Editor */} - {isFileEditor ? null : + {isFileEditor || isWorkflowEditor ? null :
{userdata !== undefined && userdata !== null && userdata.support === true ? @@ -2190,15 +2189,15 @@ const CodeEditor = (props) => { console.log("DROP: ", e) }} > - {(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor ? ( + {(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor || isWorkflowEditor ? ( {
- {isFileEditor ? null : + {isFileEditor || isWorkflowEditor ? null :
{
-
+
+
+
+ + ); + }; + const modalView = ( {
{modalView} {cloudSyncModal} + {accountDeleteButtonClicked && }
@@ -1340,7 +1562,8 @@ const TenantsTab = memo((props) => { + <> + + + {selectedOrganization?.creator_org?.length > 0 ? null : + } + + } style={{ display: "table-cell", verticalAlign: "middle" }} /> diff --git a/frontend/src/components/UserManagmentTab.jsx b/frontend/src/components/UserManagmentTab.jsx index 7671bbbb..81b65b94 100644 --- a/frontend/src/components/UserManagmentTab.jsx +++ b/frontend/src/components/UserManagmentTab.jsx @@ -1458,33 +1458,8 @@ const UserManagmentTab = memo((props) => { ); } - const getRegionFlag = (region_url) => { - let regiontag = "UK"; - let regionCode = "gb"; - const regionsplit = region_url?.split("."); - if (regionsplit?.length > 2 && !regionsplit[0]?.includes("shuffler")) { - const namesplit = regionsplit[0]?.split("/"); - regiontag = namesplit[namesplit?.length - 1]; - if (regiontag === "california") { - regiontag = "US"; - regionCode = "us"; - } else if (regiontag === "frankfurt") { - regiontag = "EU-2"; - regionCode = "eu"; - } else if (regiontag === "ca") { - regiontag = "CA"; - regionCode = "ca"; - }else if (regiontag === "au") { - regiontag = "AUS"; - regionCode = "au" - } - } - - return regionCode; - }; - - const userRegion = data?.user_geo_info?.country?.iso_code?.length > 0 ? data?.user_geo_info?.country?.iso_code : selectedOrganization?.region_url?.length > 0 ? getRegionFlag(selectedOrganization?.region_url) : "eu"; + const userRegion = data?.user_geo_info?.country?.iso_code return ( diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index d7bb0e1e..4835e4f1 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -181,6 +181,10 @@ export const getTheme = (themeMode, brandColor) => cardBackgroundColor: themeMode === "dark" ? "#1e1e1e" : "#eaeaea", cardHoverColor: themeMode === "dark" ? "#323232" : "#F0F0F0", hoverColor: themeMode === "dark" ? "#323232" : "#D6D6D6", + usecaseCardColor: themeMode === "dark" ? "#2f2f2f" : "rgba(245, 245, 245, 1)", + usecaseCardHoverColor: themeMode === "dark" ? "#2F2F2F" : "rgba(245, 245, 245, 1)", + usecaseDialogFieldColor: themeMode === "dark" ? "#2B2B2B" : "#F5F5F5", + accentColor: themeMode === "dark" ? "#ff8544" : "#ff8544", green: themeMode === "dark" ? "#5cc879" : "#008000", defaultBorder: themeMode === "dark" ? '1px solid #494949' : '1px solid #CCCCCC', linkColor: brandColor === "#ff8544" ? "#f86a3e" : brandColor, diff --git a/frontend/src/views/Admin2.jsx b/frontend/src/views/Admin2.jsx index de3143df..f94c3441 100644 --- a/frontend/src/views/Admin2.jsx +++ b/frontend/src/views/Admin2.jsx @@ -287,8 +287,8 @@ const Admin2 = (props) => { const handleStatusChange = (event) => { - const { value } = event.target; - setSelectedStatus(value); + const { value } = event.target + setSelectedStatus(value) handleEditOrg( selectedOrganization?.name, diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 7526c9d7..0aca44b5 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -6,6 +6,7 @@ import { getTheme } from "../theme.jsx"; import { useInterval } from "react-powerhooks"; import { makeStyles, } from "@mui/styles"; +import YAML from "yaml"; import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx" import { v4 as uuidv4, v5 as uuidv5, validate as isUUID, } from "uuid"; import { useNavigate, Link, useParams } from "react-router-dom"; @@ -132,6 +133,7 @@ import { ArrowForward as ArrowForwardIcon, OpenInFull as OpenInFullIcon, Difference as DifferenceIcon, + DataObject as DataObjectIcon, } from "@mui/icons-material"; import SwapHorizIcon from '@mui/icons-material/SwapHoriz'; //import * as cytoscape from "cytoscape"; @@ -778,9 +780,9 @@ const AngularWorkflow = (defaultprops) => { "name": "action", "value": "list_tickets", "options": [ + "create_ticket", "list_tickets", "get_ticket", - "create_ticket", "close_ticket", "add_comment", "update_ticket", @@ -794,16 +796,6 @@ const AngularWorkflow = (defaultprops) => { "required": false, "multiline": true, }, - /*{ - "name": "options", - "value": "deduplicate,enrich", - "required": false, - "multiselect": true, - "options": [ - "deduplicate", - "enrich", - ] - }*/ ] }, { "name": "Communication", @@ -813,9 +805,9 @@ const AngularWorkflow = (defaultprops) => { "name": "action", "value": "list_messages", "options": [ + "send_message", "list_messages", "get_message", - "send_message", "search_messages", "list_attachments", "get_attachment", @@ -838,15 +830,15 @@ const AngularWorkflow = (defaultprops) => { "parameters": [{ "name": "action", "value": "get_asset", - "options": [ - "reset_password", - "enable_user", - "disable_user", - "get_identity", - "get_asset", - "search_identity" - ], - "required": true, + "options": [ + "reset_password", + "enable_user", + "disable_user", + "get_identity", + "get_asset", + "search_identity" + ], + "required": true, }, { "name": "fields", @@ -861,9 +853,9 @@ const AngularWorkflow = (defaultprops) => { "label": "Assets", "parameters": [{ "name": "action", - "value": "search_vulnerabilities", + "value": "list_assets", "options": [ - "list_assets", + "list_assets", "get_asset", "search_assets", "search_users", @@ -915,8 +907,8 @@ const AngularWorkflow = (defaultprops) => { "value": "get_ioc", "options": [ "get_ioc", - "search_ioc", "create_ioc", + "search_ioc", "update_ioc", "delete_ioc" ], @@ -975,28 +967,9 @@ const AngularWorkflow = (defaultprops) => { "multiline": true, }] }, - - ] }] - /* - { - "name": "Email", - "label": "Email", - "parameters": [{ - "name": "action", - "value": "list_email", - "options": [ - "list_email", - "send_mail", - ], - "required": true, - }], - }] - }] - */ - // For code editor const [codeEditorModalOpen, setCodeEditorModalOpen] = React.useState(false); const [codedata, setcodedata] = React.useState(""); @@ -1213,6 +1186,7 @@ const AngularWorkflow = (defaultprops) => { if (workflow?.suborg_distribution !== undefined && workflow?.suborg_distribution !== null && workflow?.suborg_distribution.length > 0) { getChildWorkflows(workflow.id) } + }, [workflow]); // Event for making sure app is correct @@ -2506,9 +2480,11 @@ const AngularWorkflow = (defaultprops) => { console.log("Should redirect to register with redirect.") setTimeout(() => { - toast("You may not have access to this workflow.") - //window.location.href = `/register?view=/workflows/${props.match.params.key}&message=You need sign up to use workflows with Shuffle` - window.location.href = `/workflows` + // toast("You may not have access to this workflow.") + localStorage.setItem("redirectId", props.match.params.key) + navigate("/register?view=workflows&message=You need sign up to use workflows with Shuffle"); + // window.location.href = `/register?view=/workflows/${props.match.params.key}&message=You need sign up to use workflows with Shuffle` + // window.location.href = `/workflows` }, 2500) return @@ -2878,9 +2854,9 @@ const AngularWorkflow = (defaultprops) => { setSavingState(0); console.log("Workflow failed loading: ", responseJson); if (responseJson.reason !== undefined && responseJson.reason !== null) { - toast("Failed to save: " + responseJson.reason); + toast.error("Failed to save: " + responseJson.reason); } else { - toast(`Failed to save. Please contact your ${supportEmail} or your local admin if this is unexpected.`) + toast.error(`Failed to save. Please contact your ${supportEmail} or your local admin if this is unexpected.`) } } else { setSavingState(1); @@ -4590,6 +4566,9 @@ const AngularWorkflow = (defaultprops) => { } setTimeout(() => { + if(!isLoggedIn){ + localStorage.setItem("redirectId", props.match.params.key); + } window.location.pathname = "/workflows"; }, 2500); @@ -4880,6 +4859,17 @@ const AngularWorkflow = (defaultprops) => { setOriginalWorkflow(responseJson) } + const tmpUi = new URLSearchParams(cursearch).get("ui"); + if ( + tmpUi !== undefined && + tmpUi !== null && + tmpUi === "yaml" + ) { + setTimeout(() => { + setupWorkflowYaml(responseJson) + }, 2500) + } + setWorkflow(responseJson); setWorkflowDone(true); @@ -4898,6 +4888,9 @@ const AngularWorkflow = (defaultprops) => { setConfigureWorkflowModalOpen(true) } + + + } }) .catch((error) => { @@ -9959,6 +9952,191 @@ const AngularWorkflow = (defaultprops) => { }); } + const setupWorkflowYaml = (inputworkflow) => { + toast.warn("YAML exploring is an experimental feature - only visible to support users. The goal of this is to make it EASY to edit the workflow as YAML instead of just using the UI") + if (userdata?.support !== true) { + console.log("Not support: ", userdata) + return + } + + // This should be getting the workflow based on actual nodes in the workflow + // as the goal is to have it be live + var copiedWorkflow = JSON.parse(JSON.stringify(inputworkflow)) + + const removeObjects = [ + "execution_org", + "categories", + "example_argument", + "public", + "contact_info", + "published_id", + "revision_id", + "usecase_ids", + "input_questions", + "form_control", + "blogpost", + "video", + "status", + "generated", + "hidden", + "updated_by", + "validated", + "validation", + "childorg_workflow_ids", + "backup_config", + "auth_groups", + "isValid", + "workflow_as_code", + "image", + "sharing", + "owner", + "configuration", + "created", + "edited", + "last_runtime", + "due_date", + "is_valid", + "execution_environment", + "default_return_value", + "visual_branches", + "previously_saved", + "workflow_type", + "parentorg_workflow", + "suborg_distribution", + "id", + "comments", + "org_id", + + // Failing test + "spalabi", + ] + + for (var i = 0; i < removeObjects.length; i++) { + delete copiedWorkflow[removeObjects[i]] + } + + const removeActionValues = [ + "_id", + "id", + + "large_image", + "description", + "is_valid", + "isStartNode", + "sharing", + "public", + "generated", + "execution_variable", + "position", + "category", + "reference_url", + "sub_action", + "run_magic_output", + "run_magic_input", + "category_label", + "suggestions", + "parent_controlled", + "source_workflow", + "source_executions", + "app_association", + "suggestion", + "small_image", + "long_description", + "tags", + "errors", + "source_executions", + "source_execution", + "required", + "example", + "type", + + "test2", + ] + + const removeActionParamValues = [ + "id", + "multiline", + "multiselect", + "options", + "action_field", + "variant", + "configuration", + "tags", + "schema", + "skip_multicheck", + "value_replace", + "unique_toggled", + "hidden", + "error", + "example", + + "test3", + ] + + if (copiedWorkflow?.actions !== undefined && copiedWorkflow?.actions !== null && copiedWorkflow?.actions.length > 0) { + for (var key in copiedWorkflow.actions) { + for (var i = 0; i < removeActionValues.length; i++) { + delete copiedWorkflow.actions[key][removeActionValues[i]] + } + + + if (copiedWorkflow.actions[key].parameters === undefined || copiedWorkflow.actions[key].parameters === null) { + continue + } + + for (var j = 0; j < copiedWorkflow.actions[key].parameters.length; j++) { + for (var k = 0; k < removeActionParamValues.length; k++) { + delete copiedWorkflow.actions[key].parameters[j][removeActionParamValues[k]] + } + } + } + } + + if (copiedWorkflow?.triggers !== undefined && copiedWorkflow?.triggers !== null && copiedWorkflow?.triggers.length > 0) { + for (var key in copiedWorkflow.triggers) { + for (var i = 0; i < removeActionValues.length; i++) { + delete copiedWorkflow.triggers[key][removeActionValues[i]] + } + + delete copiedWorkflow.triggers[key]["app_name"] + delete copiedWorkflow.triggers[key]["app_version"] + delete copiedWorkflow.triggers[key]["name"] + delete copiedWorkflow.triggers[key]["priority"] + delete copiedWorkflow.triggers[key]["replacement_for_trigger"] + + if (copiedWorkflow.triggers[key].parameters === undefined || copiedWorkflow.triggers[key].parameters === null) { + continue + } + + for (var j = 0; j < copiedWorkflow.triggers[key].parameters.length; j++) { + for (var k = 0; k < removeActionParamValues.length; k++) { + delete copiedWorkflow.triggers[key].parameters[j][removeActionParamValues[k]] + } + } + } + } + + if (copiedWorkflow?.workflow_variables === undefined || copiedWorkflow?.workflow_variables === null || copiedWorkflow?.workflow_variables.length === 0) { + delete copiedWorkflow.workflow_variables + } + + if (copiedWorkflow?.branches === undefined || copiedWorkflow?.branches === null || copiedWorkflow?.branches.length === 0) { + delete copiedWorkflow.branches + } + + + // YAML + const sampledata = YAML.stringify(copiedWorkflow) + + navigate("?ui=yaml", { replace: true }) + + setCodeEditorModalOpen(true) + setEditorData({ + "name": "workflow yaml", + "value": sampledata, + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps //useEffect(() => { if (firstrequest) { @@ -10000,6 +10178,7 @@ const AngularWorkflow = (defaultprops) => { //navigate(`?execution_highlight=${parsed_url}`) //props.history.push(curpath + newitem); } + return; } @@ -12136,10 +12315,8 @@ const AngularWorkflow = (defaultprops) => { } if ((app.id === "integration" || app.id === "shuffle_agent") && userdata.support !== true) { - console.log("APPID: ", app.id, isCloud) if (isCloud === false && app.id === "integration") { } else { - console.log("RETURNING", app.id) return null } } @@ -12572,7 +12749,7 @@ const AngularWorkflow = (defaultprops) => { // maxiter = max amount of parent nodes to loop // also handles breaks if there are issues var iterations = 0; - var maxiter = 10; + var maxiter = 100; while (true) { for (let parentkey in allkeys) { var currentnode = cy.getElementById(allkeys[parentkey]); @@ -18553,6 +18730,68 @@ const AngularWorkflow = (defaultprops) => { zoom: isSafari ? undefined : 0.9, } + + const changeOrg = () => { + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); + fetch(`${globalUrl}/api/v1/orgs/${workflow.org_id}/change`, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify({ "org_id": workflow.org_id }), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } else { + localStorage.removeItem("apps") + localStorage.removeItem("workflows") + localStorage.removeItem("userinfo") + } + + return response.json(); + }) + .then(function (responseJson) { + console.log("In here?") + if (responseJson.success === true) { + if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { + console.log("Region Change: ", responseJson.region_url); + localStorage.setItem("globalUrl", responseJson.region_url); + //globalUrl = responseJson.region_url + } + + if (responseJson["reason"] === "SSO_REDIRECT") { + setTimeout(() => { + toast.info("Redirecting to SSO login page as SSO is required for this organization.") + window.location.href = responseJson["url"] + return + }, 2000) + } else { + setTimeout(() => { + window.location.reload(); + }, 2000); + } + + toast.info("Successfully changed active organisation - refreshing!"); + } else { + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { + toast(responseJson.reason); + } else { + toast(`Failed changing org. Try again or contact ${supportEmail} if this persists.`); + } + } + }) + .catch((error) => { + console.log("error changing: ", error); + //removeCookie("session_token", {path: "/"}) + }) + } + const TopCytoscapeBar = (props) => { const [hovered, setHovered] = useState(false) @@ -18620,72 +18859,15 @@ const AngularWorkflow = (defaultprops) => { )} - {!distributedFromParent ? - isCorrectOrg ? null : + {!distributedFromParent || userdata?.support === true ? + isCorrectOrg ? null : Warning: { toast("Changing to correct organisation. Please wait a few seconds.") - localStorage.setItem("globalUrl", ""); - localStorage.setItem("getting_started_sidebar", "open"); - fetch(`${globalUrl}/api/v1/orgs/${workflow.org_id}/change`, { - mode: "cors", - credentials: "include", - crossDomain: true, - method: "POST", - body: JSON.stringify({ "org_id": workflow.org_id }), - withCredentials: true, - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - }) - .then(function (response) { - if (response.status !== 200) { - console.log("Error in response"); - } else { - localStorage.removeItem("apps") - localStorage.removeItem("workflows") - localStorage.removeItem("userinfo") - } - - return response.json(); - }) - .then(function (responseJson) { - console.log("In here?") - if (responseJson.success === true) { - if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { - console.log("Region Change: ", responseJson.region_url); - localStorage.setItem("globalUrl", responseJson.region_url); - //globalUrl = responseJson.region_url - } - - if (responseJson["reason"] === "SSO_REDIRECT") { - setTimeout(() => { - toast.info("Redirecting to SSO login page as SSO is required for this organization.") - window.location.href = responseJson["url"] - return - }, 2000) - } else { - setTimeout(() => { - window.location.reload(); - }, 2000); - } - - toast("Successfully changed active organisation - refreshing!"); - } else { - if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { - toast(responseJson.reason); - } else { - toast(`Failed changing org. Try again or contact ${supportEmail} if this persists.`); - } - } - }) - .catch((error) => { - console.log("error changing: ", error); - //removeCookie("session_token", {path: "/"}) - }) + changeOrg() @@ -18707,6 +18889,11 @@ const AngularWorkflow = (defaultprops) => {
{originalWorkflow?.parentorg_workflow !== undefined && originalWorkflow?.parentorg_workflow !== null && originalWorkflow?.parentorg_workflow.length > 0 || workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0 ? + null + + /* + * Disabled because redirects occurred anyway + + */ : null} - {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 1 && workflow?.id !== undefined && workflow?.id && workflow?.id?.length > 0 ? + {userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 1 && workflow?.id !== undefined && workflow?.id && workflow?.id?.length > 0 && userdata?.active_org?.creator_org?.length === 0 && userdata?.active_org?.id == workflow?.org_id ? + + + {userdata?.support === true ? + + + + + + : null} @@ -20408,10 +20621,11 @@ const AngularWorkflow = (defaultprops) => { position, backgroundcolor: "#1f2023", color: "#ffffff", - textHalign: "center", - textValign: "center", - textMarginX: "0px", - textMarginY: "0px", + + textHalign: "right", + textValign: "bottom", + textMarginX: "-250px", + textMarginY: "-150px", }, position, }); @@ -25509,6 +25723,7 @@ const AngularWorkflow = (defaultprops) => { setAiQueryModalOpen={setAiQueryModalOpen} + isWorkflowEditor={editorData?.name === "workflow yaml"} /> : null} diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 1a4c5104..1eaf5fcb 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -745,7 +745,7 @@ const AppCreator = (defaultprops) => { } setBasedata(data); - console.log("Info: ", data) + console.log("Loaded Info: ", data) try { if (data.info !== null && data.info !== undefined) { @@ -759,7 +759,12 @@ const AppCreator = (defaultprops) => { if (data.info.title.length > 29) { setName(data.info.title.slice(0, 29)); } else { - setName(data.info.title); + // Check if fork=true in URL + if (window.location.search.includes("fork=true")) { + setName(data.info.title + " Fork"); + } else { + setName(data.info.title) + } } } diff --git a/frontend/src/views/AppExplorer.jsx b/frontend/src/views/AppExplorer.jsx index 63c81d63..f0069e4f 100644 --- a/frontend/src/views/AppExplorer.jsx +++ b/frontend/src/views/AppExplorer.jsx @@ -4066,7 +4066,7 @@ const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"; } - {isMobile ? null : ( + {isMobile || app?.reference_org === userdata?.active_org?.id ? null : (
@@ -165,7 +165,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, marginLeft: 8, color: theme.palette.text.secondary, }}> - {data.categories ? data.categories.join(", ") : "NA"} + {data?.categories ? data?.categories?.join(", ") : "NA"}
- {data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => ( + {data?.generated !== true && data?.tags && data?.tags?.slice(0, 2).map((tag, tagIndex) => ( {tag} {tagIndex < data.tags.length - 1 ? ", " : ""} diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index b2cb9426..b717b9f6 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -447,20 +447,27 @@ const LoginPage = props => { } if (isLoggedIn === true && serverside !== true) { - const tmpView = new URLSearchParams(window.location.search).get("view"); - if (tmpView !== undefined && tmpView !== null) { - let pathOnly = tmpView.split("?")[0]; - if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly; + setTimeout(() => { + const tmpView = new URLSearchParams(window.location.search).get("view"); + if (tmpView !== undefined && tmpView !== null) { + let pathOnly = tmpView.split("?")[0]; + if (!pathOnly.startsWith("/")) pathOnly = "/" + pathOnly; - if (pathOnly === "/pricing" || pathOnly === "admin") { - window.location.replace(pathOnly + window.location.search); - } else { - window.location.replace(pathOnly); + if (pathOnly === "/pricing" || pathOnly === "admin") { + window.location.replace(pathOnly + window.location.search); + } else { + if(localStorage.getItem("redirectId") !== null && localStorage.getItem("redirectId") !== undefined) { + const redirectId = localStorage.getItem("redirectId") + localStorage.removeItem("redirectId") + pathOnly = pathOnly + "/" + redirectId + } + window.location.replace(pathOnly); + } + return; } - return; - } - window.location.pathname = "/workflows" + window.location.pathname = "/workflows" + }, 2000); } const checkAdmin = () => { @@ -537,51 +544,52 @@ const LoginPage = props => { 'Content-Type': 'application/json; charset=utf-8', }, }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for login:O!"); + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for login:O!"); + } + + return response.json(); + }) + .then((responseJson) => { + + setLoginLoading(false) + + if (responseJson["success"] === false) { + setLoginInfo(responseJson["reason"]) + } else { + if (responseJson?.region_url !== undefined && responseJson?.region_url !== null && responseJson?.region_url !== "") { + toast.info("Set region to " + responseJson.region_url) + localStorage.setItem("globalUrl", responseJson.region_url) } - return response.json(); - }) - .then((responseJson) => { + if (responseJson["reason"] === "MFA_REDIRECT") { + setLoginInfo("Enter the 6-digit MFA code.") + setMFAField(true) + return - setLoginLoading(false) - - console.log("Resp from backend: ", responseJson) - - if (responseJson["success"] === false) { + } + else if (responseJson["reason"] === "MFA_SETUP") { + window.location.href = `/login/${responseJson.url}/mfa-setup`; + return; + } + else if (responseJson["reason"] === "SSO_REDIRECT") { + //navigate(responseJson["url"]) + window.location.href = responseJson["url"] + return + } + else if (responseJson["reason"] !== undefined && responseJson["reason"] !== null && responseJson["reason"].includes("error")) { setLoginInfo(responseJson["reason"]) + return } - else { - - if (responseJson["reason"] === "MFA_REDIRECT") { - setLoginInfo("Enter the 6-digit MFA code.") - setMFAField(true) - return - - } - else if (responseJson["reason"] === "MFA_SETUP") { - window.location.href = `/login/${responseJson.url}/mfa-setup`; - return; - } - else if (responseJson["reason"] === "SSO_REDIRECT") { - //navigate(responseJson["url"]) - window.location.href = responseJson["url"] - return - - } - else if (responseJson["reason"] !== undefined && responseJson["reason"] !== null && responseJson["reason"].includes("error")) { - setLoginInfo(responseJson["reason"]) - return - } - setLoginInfo("Successful login! Redirecting you in 3 seconds...") - for (var key in responseJson["cookies"]) { - setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) - } + setLoginInfo("Successful login! Redirecting you in 3 seconds...") + for (var key in responseJson["cookies"]) { + setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) + } + setTimeout(() => { const tmpView = new URLSearchParams(window.location.search).get("view"); if (tmpView !== undefined && tmpView !== null) { let pathOnly = tmpView.split("?")[0]; @@ -590,18 +598,21 @@ const LoginPage = props => { if (pathOnly === "/pricing" || pathOnly === "admin") { window.location.replace(pathOnly + window.location.search); } else { + if(localStorage.getItem("redirectId") !== null && localStorage.getItem("redirectId") !== undefined) { + const redirectId = localStorage.getItem("redirectId") + localStorage.removeItem("redirectId") + pathOnly = pathOnly + "/" + redirectId + } window.location.replace(pathOnly); } return; } - console.log("LOGIN DATA: ", responseJson) if (responseJson.tutorials !== undefined && responseJson.tutorials !== null) { // Find welcome in responseJson.tutorials under key name const welcome = responseJson.tutorials.find(function (element) { return element.name === "welcome"; }) - console.log("Welcome: ", welcome) if (welcome === undefined || welcome === null) { console.log("RUN login Welcome!!") @@ -613,12 +624,13 @@ const LoginPage = props => { } window.location.pathname = "/workflows" - } - }) - .catch(error => { - setLoginInfo("Error from login API: " + error) - setLoginLoading(false) - }); + }, 2000); + } + }) + .catch(error => { + setLoginInfo("Error from login API: " + error) + setLoginLoading(false) + }); } else { url = baseurl + '/api/v1/register'; fetch(url, { @@ -641,11 +653,15 @@ const LoginPage = props => { //setLoginInfo("Successful register!") //var newpath = "/login?message=Successfully signed up. You can now sign in." //const tmpMessage = new URLSearchParams(window.location.search).get("message") - setLoginInfo("Successful registration! Redirecting in 3 seconds...") + for (var key in responseJson["cookies"]) { setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" }) } + setLoginLoading(false) + setLoginInfo("Successful registration! Redirecting in 3 seconds...") + + setTimeout(() => { console.log("LOGIN DATA: ", responseJson) @@ -657,6 +673,11 @@ const LoginPage = props => { if (pathOnly === "/pricing" || pathOnly === "admin") { window.location.replace(pathOnly + window.location.search); } else { + if(localStorage.getItem("redirectId") !== null && localStorage.getItem("redirectId") !== undefined) { + const redirectId = localStorage.getItem("redirectId") + localStorage.removeItem("redirectId") + pathOnly = pathOnly + "/" + redirectId + } window.location.replace(pathOnly); } return @@ -666,9 +687,8 @@ const LoginPage = props => { console.log("RUN Welcome!!") //window.location.pathname = "/welcome?tab=2" window.location.href = "/welcome" - }, 1500); + }, 2000); } - setLoginLoading(false) }), ) .catch(error => { diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 9753bfb0..d702f24f 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -3,6 +3,7 @@ import React, { useEffect, useContext, memo, useState, useRef } from "react"; import { useLocation, useNavigate, Link } from "react-router-dom"; import ReactDOM from "react-dom" import { getTheme } from "../theme.jsx"; + // Material UI Icons import Add from '@mui/icons-material/Add'; import Search from '@mui/icons-material/Search'; @@ -12,6 +13,7 @@ import GridOnIcon from '@mui/icons-material/GridOn'; import ListIcon from '@mui/icons-material/List'; import PublishIcon from '@mui/icons-material/Publish'; import GetAppIcon from '@mui/icons-material/GetApp'; + // Material UI & Components import { makeStyles } from "@mui/styles"; import { Navigate } from "react-router-dom"; @@ -92,6 +94,11 @@ import { Visibility as VisibilityIcon, EditNote as EditNoteIcon, ErrorOutline as ErrorOutlineIcon, + Coronavirus as CoronavirusIcon, + Fingerprint as FingerprintIcon, + Psychology as PsychologyIcon, + Wifi as WifiIcon, + Devices as DevicesIcon, } from "@mui/icons-material"; // Additional Components @@ -128,19 +135,20 @@ const imagesize = 22; export const GetIconInfo = (action) => { // Finds the icon based on the action. Should be verbs. const iconList = [ - { key: "cases", values: ["cases"] }, + { key: "cases", values: ["cases", "ticket", "alert"] }, { key: "cache_add", values: ["set_cache"] }, { key: "cache_get", values: ["get_cache"] }, { key: "filter", values: ["filter"] }, { key: "merge", values: ["join", "merge", "route", "router"] }, { key: "search", - values: ["search", "find", "locate", "index", "analyze", "anal", "match", "check cache", "check", "verify", "validate"], + values: ["search", "find", "locate", "index", "analyze", "anal", "match", "check cache", "check", "verify", "validate", "siem", ], }, { key: "list", values: ["list", "head", "options"] }, { key: "download", values: [ + "ingest", "capture", "get", "download", @@ -154,20 +162,6 @@ export const GetIconInfo = (action) => { }, { key: "add", values: ["add", "accept",] }, { key: "delete", values: ["delete", "remove", "clear", "clean", "dismiss",] }, - { - key: "send", - values: [ - "send", - "dispatch", - "mail", - "forward", - "post", - "submit", - "mark", - "set", - "release", - ], - }, { key: "repeat", values: ["repeat", "retry", "pause", "skip", "copy", "replicat", "demo",], @@ -197,8 +191,29 @@ export const GetIconInfo = (action) => { key: "compare", values: ["compare", "convert", "to", "filter", "translate", "parse"], }, + { key: "assets", values: ["cmdb", "assets", "asset", "cmdb", "inventory", "host", "hosts", "device", "devices"] }, { key: "close", values: ["close", "stop", "cancel", "block"] }, { key: "communication", values: ["communication", "comms", "email", "mail",] }, + { key: "eradication", values: ["eradication", "edr", "xdr"] }, + { key: "iam", values: ["iam", "identity", "access", "auth", "authentication", "authorization", "oauth", "sso", "openid"] }, + { key: "intel", values: ["intel", "feed", "threat intel", "threat intelligence", "ti", "t.i.", "t.i", "ti.", "rule", "technique", "tactic", "techniques", "tactics", "ioc", "indicator",] }, + { key: "network", values: ["network", "net", "networking", "firewall", "proxy", "vpn", "sdwan", "sd-wan"] }, + { + key: "send", + values: [ + "send", + "dispatch", + "mail", + "forward", + "post", + "submit", + "mark", + "set", + "release", + ], + }, + + ]; var selectedKey = "" @@ -357,7 +372,45 @@ export const GetIconInfo = (action) => { iconBackgroundColor: "green", originalIcon: , }, - }; + eradication: { + icon: "", + iconColor: "white", + iconBackgroundColor: "green", + originalIcon: , + }, + iam: { + icon: "", + iconColor: "white", + iconBackgroundColor: "green", + originalIcon: , + }, + intel: { + icon: "", + iconColor: "white", + iconBackgroundColor: "green", + originalIcon: , + }, + network: { + icon: "", + iconColor: "white", + iconBackgroundColor: "green", + originalIcon: , + }, + assets: { + icon: "", + iconColor: "white", + iconBackgroundColor: "green", + originalIcon: , + }, + } + + /* + { key: "eradication", values: ["eradication", "edr", "xdr"] }, + { key: "iam", values: ["iam", "identity", "access", "auth", "authentication", "authorization", "oauth", "sso", "openid"] }, + { key: "intel", values: ["intel", "threat intel", "threat intelligence", "ti", "t.i.", "t.i", "ti."] }, + { key: "network", values: ["network", "net", "networking", "firewall", "proxy", "vpn", "sdwan", "sd-wan"] }, + { key: "siem", values: ["siem", "security information and event management", "security information and event management", "security information and event management"] }, + */ var selectedItem = parsedIcons[selectedKey]; if (selectedItem === undefined || selectedItem === null) { @@ -374,11 +427,8 @@ export const GetIconInfo = (action) => { selectedItem.iconBackgroundColor = defaultColor; } - if (selectedItem.icon === "" || selectedItem.icon === undefined) { - console.log( - `MISSING PATH FOR ${selectedKey} (find in scope): `, - selectedItem.originalIcon.type.type - ); + if ((selectedItem.icon === "" || selectedItem.icon === undefined) && (selectedItem.originalIcon === undefined || selectedItem.originalIcon === "")) { + console.log(`MISSING PATH FOR ${selectedKey} (find in scope): `, selectedItem.originalIcon.type.type) } if ( @@ -753,6 +803,12 @@ const Workflows2 = (props) => { const handleTabChange = (event, newValue) => { setCurrTab(newValue); + + //if (view === "list" && currTab > 0) { + // setView("grid") + // setUpdate(Math.random()) + //} + // Update URL query params based on tab index const tabMapping = { 0: 'org_workflows', @@ -767,9 +823,6 @@ const Workflows2 = (props) => { navigate(`${location.pathname}?${queryParams.toString()}`); }; - - - const handleCreateWorkflow = () => { setModalOpen(true) setIsEditing(false) @@ -1854,9 +1907,9 @@ const Workflows2 = (props) => { data = JSON.parse(JSON.stringify(data)); data = sanitizeWorkflow(data); toast("Sanitizing and publishing " + data.name); - + // This ALWAYS talks to Shuffle cloud - fetch(globalUrl + "/api/v1/workflows/" + data.id + "/publish", { + fetch(globalUrl + "/api/v1/workflows/" + data.id + "/publish" , { method: "POST", headers: { "Content-Type": "application/json", @@ -3414,6 +3467,8 @@ const Workflows2 = (props) => { return obj; }) + console.log("ROWS: ", rows) + workflowData = ( { onClick={() => { localStorage.setItem("view", "list"); setView("list"); + + setCurrTab(0) }} > @@ -4005,20 +4062,30 @@ const Workflows2 = (props) => { // } useEffect(() => { - if (currTab === 2) return; - if (userdata !== undefined && userdata !== null && filteredWorkflows.length > 0) { + if (filteredWorkflows.length > 0) { + var categoryWorkflows = [] if (currTab === 0) { categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) - setOrgWorkflows(categoryWorkflows) + + if (categoryWorkflows.length === 0) { + setOrgWorkflows(filteredWorkflows) + } else { + setOrgWorkflows(categoryWorkflows) + } } else if (currTab === 1) { categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id) setMyWorkflows(categoryWorkflows) } + setIsLoadingWorkflow(false); - } + } else { + if (currTab === 0 && workflows?.length > 0) { + setOrgWorkflows(workflows) + } + } }, [currTab, workflows, userdata, filteredWorkflows, filters]) @@ -4607,13 +4674,17 @@ const Workflows2 = (props) => { - + { const newView = view === "grid" ? "list" : "grid"; localStorage.setItem("workflowView", newView); setView(newView); + + if (view === "grid") { + setCurrTab(0) + } }} disabled={currTab === 2} > @@ -4699,25 +4770,30 @@ const Workflows2 = (props) => { paddingBottom: 40 }}> - {currTab === 0 && orgWorkflows.map((data, index) => { - // Shouldn't be a part of this list - if (data.public === true) { - return null - } + {currTab !== 0 ? null : + orgWorkflows.length === 0 ? + + No workflows found in this org with the Org ID filter. If this is an error, please click the "List View" button at the top to see ALL available workflows ({workflows.length}). + : + orgWorkflows.map((data, index) => { + // Shouldn't be a part of this list + if (data.public === true) { + return null + } - // if (firstLoad) { - // workflowDelay += 75 - // } else { - // return - // } + // if (firstLoad) { + // workflowDelay += 75 + // } else { + // return + // } - return ( - - {/**/} - - {/**/} - - ) + return ( + + {/**/} + + {/**/} + + ) })} {currTab === 3 && backupWorkflows.map((data, index) => { @@ -4742,24 +4818,29 @@ const Workflows2 = (props) => { })} { - currTab === 1 && myWorkflows.map((data, index) => { - if (data.public === true) { - return null - } + currTab !== 1 ? null : + myWorkflows.length === 0 ? + + No workflows found in this org for your user. + : + myWorkflows.map((data, index) => { + if (data.public === true) { + return null + } - // if (firstLoad) { - // workflowDelay += 75 - // } else { - // return - // } + // if (firstLoad) { + // workflowDelay += 75 + // } else { + // return + // } - return ( - - {/**/} - - {/**/} - - ) + return ( + + {/**/} + + {/**/} + + ) }) }
@@ -5123,7 +5204,7 @@ const Workflows2 = (props) => { //isLoaded && isLoggedIn && workflowDone ? ( const loadedCheck = - workflowDone ? ( + workflowDone && isLoaded ? (
{/* From 7cf93ecfdc0e26f7351d81cce8a07da674e967a3 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 17 Jun 2025 20:59:22 +0200 Subject: [PATCH 194/200] Orborus/worker rerun --- functions/onprem/orborus/go.mod | 2 +- functions/onprem/orborus/go.sum | 4 +- functions/onprem/worker/go.mod | 133 +++++++------ functions/onprem/worker/go.sum | 335 +++++++++++++++++--------------- 4 files changed, 255 insertions(+), 219 deletions(-) diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 84fdc28a..5d3ee295 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/docker v28.2.2+incompatible github.com/docker/go-connections v0.5.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.82 + github.com/shuffle/shuffle-shared v0.8.84 k8s.io/api v0.33.1 k8s.io/apimachinery v0.33.1 ) diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index 529fc6ee..13ae08b5 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -324,8 +324,8 @@ github.com/sendgrid/sendgrid-go v3.16.1+incompatible h1:zWhTmB0Y8XCDzeWIm2/BIt1G github.com/sendgrid/sendgrid-go v3.16.1+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.82 h1:vwXG/NOZNoPMfbF14ZnygoY8+AI2QVsggWfgfpSbqzo= -github.com/shuffle/shuffle-shared v0.8.82/go.mod h1:RdfNxqCPI+zU4jQKy3E/p4Io2injm7LpSKQUCDHNtLk= +github.com/shuffle/shuffle-shared v0.8.84 h1:ElIMQYjKBVOiadbiGkSzt/lPU5xqaQwRxQvk9wx/xYM= +github.com/shuffle/shuffle-shared v0.8.84/go.mod h1:RdfNxqCPI+zU4jQKy3E/p4Io2injm7LpSKQUCDHNtLk= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index de3fd5c5..1df6cb59 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -1,128 +1,142 @@ module worker -go 1.23.0 +go 1.24.0 -toolchain go1.23.1 +toolchain go1.24.4 //replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared require ( - github.com/docker/docker v28.0.4+incompatible + github.com/docker/docker v28.2.2+incompatible github.com/gorilla/mux v1.8.1 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.58 - k8s.io/api v0.32.3 - k8s.io/apimachinery v0.32.3 - k8s.io/client-go v0.32.3 + github.com/shuffle/shuffle-shared v0.8.84 + k8s.io/api v0.33.1 + k8s.io/apimachinery v0.33.1 + k8s.io/client-go v0.33.1 ) require ( - cloud.google.com/go/auth v0.15.0 // indirect + cloud.google.com/go/auth v0.16.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect ) require ( - cloud.google.com/go v0.112.2 // indirect - cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/datastore v1.15.0 // indirect - cloud.google.com/go/iam v1.1.6 // indirect - cloud.google.com/go/scheduler v1.10.6 // indirect - cloud.google.com/go/storage v1.39.1 // indirect + cel.dev/expr v0.20.0 // indirect + cloud.google.com/go v0.121.1 // indirect + cloud.google.com/go/compute/metadata v0.7.0 // indirect + cloud.google.com/go/datastore v1.20.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/monitoring v1.24.2 // indirect + cloud.google.com/go/scheduler v1.11.7 // indirect + cloud.google.com/go/storage v1.55.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v1.1.3 // indirect - github.com/adrg/strutil v0.2.3 // indirect - github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect - github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/adrg/strutil v0.3.1 // indirect + github.com/algolia/algoliasearch-client-go/v3 v3.31.4 // indirect + github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cloudflare/circl v1.3.7 // indirect - github.com/cyphar/filepath-securejoin v0.2.5 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudflare/circl v1.6.1 // indirect + github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/frikky/kin-openapi v0.41.0 // indirect + github.com/frikky/kin-openapi v0.42.0 // indirect github.com/frikky/schemaless v0.0.16 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.0 // indirect - github.com/go-git/go-git/v5 v5.13.0 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.16.1 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-github/v28 v28.1.1 // indirect - github.com/google/go-querystring v1.0.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.14.1 // indirect + github.com/googleapis/gax-go/v2 v2.14.2 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opensearch-project/opensearch-go v1.1.0 // indirect github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sashabaranov/go-openai v1.40.0 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/sashabaranov/go-openai v1.40.1 // indirect github.com/sendgrid/rest v2.6.9+incompatible // indirect - github.com/sendgrid/sendgrid-go v3.14.0+incompatible // indirect + github.com/sendgrid/sendgrid-go v3.16.1+incompatible // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/skeema/knownhosts v1.3.0 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - go.opencensus.io v0.24.0 // indirect + github.com/zeebo/errs v1.4.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect - go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.26.0 // indirect - google.golang.org/api v0.228.0 // indirect + google.golang.org/api v0.236.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect - google.golang.org/grpc v1.71.0 // indirect + google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect + google.golang.org/grpc v1.72.2 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -130,9 +144,10 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum index ba4f34c4..0ef2d3a6 100644 --- a/functions/onprem/worker/go.sum +++ b/functions/onprem/worker/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= +cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -7,29 +9,37 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw= -cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= +cloud.google.com/go v0.121.1 h1:S3kTQSydxmu1JfLRLpKtxRPA7rSrYPRPEUmL/PavVUw= +cloud.google.com/go v0.121.1/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.15.0 h1:0P9WcsQeTWjuD1H14JIY7XQscIPQ4Laje8ti96IC5vg= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/datastore v1.20.0 h1:NNpXoyEqIJmZFc0ACcwBEaXnmscUpcG4NkKnbCePmiM= +cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaYNaPAKpfh8Ew= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= +cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= +cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/scheduler v1.10.6 h1:5U8iXLoQ03qOB+ZXlAecU7fiE33+u3QiM9nh4cd0eTE= -cloud.google.com/go/scheduler v1.10.6/go.mod h1:pe2pNCtJ+R01E06XCDOJs1XvAMbv28ZsQEbqknxGOuE= +cloud.google.com/go/scheduler v1.11.7 h1:zkMEJ0UbEJ3O7NwEUlKLIp6eXYv1L7wHjbxyxznajKM= +cloud.google.com/go/scheduler v1.11.7/go.mod h1:gqYs8ndLx2M5D0oMJh48aGS630YYvC432tHCnVWN13s= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.39.1 h1:MvraqHKhogCOTXTlct/9C3K3+Uy2jBmFYb3/Sp6dVtY= -cloud.google.com/go/storage v1.39.1/go.mod h1:xK6xZmxZmo+fyP7+DEF6FhNc24/JAe95OLyOHCXFH1o= +cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2znf0= +cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY= +cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= +cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -37,17 +47,25 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEK github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= -github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= -github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= -github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= -github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/adrg/strutil v0.3.1 h1:OLvSS7CSJO8lBii4YmBt8jiK9QOtB9CzCzwl4Ic/Fz4= +github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA= +github.com/algolia/algoliasearch-client-go/v3 v3.31.4 h1:UJhx6AhZCYf0qZygDz2c1x1+1q2q2sfzsRaQM6yswWk= +github.com/algolia/algoliasearch-client-go/v3 v3.31.4/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -66,51 +84,64 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3w github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= -github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= +github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I= +github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= +github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= -github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= -github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= -github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frikky/kin-openapi v0.41.0 h1:oMmjo+ekGS971lb3KLeZZOqRDZOwWi3+g/OiSWP08+s= -github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= +github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= +github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/schemaless v0.0.16 h1:4d2ZktB9xGsAusbbKliOI8TuriSrdIMzD/6ToY3wkz8= github.com/frikky/schemaless v0.0.16/go.mod h1:jT48kTcmr1q3o8i+8qe7g+eCsbwaz2Q9CjOJevQQzQs= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -121,14 +152,16 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= -github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= -github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= +github.com/go-git/go-git/v5 v5.16.1 h1:TuxMBWNL7R05tXsUGi0kh1vi4tq0WfXNLlIrAkXG1k8= +github.com/go-git/go-git/v5 v5.16.1/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -152,8 +185,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -162,27 +195,19 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -190,15 +215,14 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -207,15 +231,14 @@ github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAx github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= -github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= +github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= +github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= @@ -251,6 +274,10 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -268,50 +295,55 @@ github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo= github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= -github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/sashabaranov/go-openai v1.40.0 h1:Peg9Iag5mUJtPW00aYatlsn97YML0iNULiLNe74iPrU= -github.com/sashabaranov/go-openai v1.40.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= +github.com/sashabaranov/go-openai v1.40.1 h1:bJ08Iwct5mHBVkuvG6FEcb9MDTfsXdTYPGjYLRdeTEU= +github.com/sashabaranov/go-openai v1.40.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= -github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA= -github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= +github.com/sendgrid/sendgrid-go v3.16.1+incompatible h1:zWhTmB0Y8XCDzeWIm2/BIt1GjJohAA0p6hVEaDtHWWs= +github.com/sendgrid/sendgrid-go v3.16.1+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.58 h1:QzCKtQjuaozb+xyLkw6IjwqCNVWzpuJJnxkpXkLCP9M= -github.com/shuffle/shuffle-shared v0.8.58/go.mod h1:OLAwH/Ym4941Jn5DF1oZaq6iBpmjG2SNrTZ9Xqck5So= +github.com/shuffle/shuffle-shared v0.8.84 h1:ElIMQYjKBVOiadbiGkSzt/lPU5xqaQwRxQvk9wx/xYM= +github.com/shuffle/shuffle-shared v0.8.84/go.mod h1:RdfNxqCPI+zU4jQKy3E/p4Io2injm7LpSKQUCDHNtLk= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= -github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -321,6 +353,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -330,36 +363,40 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= -go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= -go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= +go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= +go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -367,8 +404,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -396,8 +433,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -411,25 +446,23 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -438,8 +471,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -453,7 +486,6 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -465,14 +497,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -483,8 +515,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= @@ -522,8 +554,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -532,8 +562,8 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.228.0 h1:X2DJ/uoWGnY5obVjewbp8icSL5U4FzuCfy9OjbLSnLs= -google.golang.org/api v0.228.0/go.mod h1:wNvRS1Pbe8r4+IfBIniV8fwCpGwTrYa+kMUDiC5z5a4= +google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0= +google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -554,33 +584,21 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= +google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= -google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= @@ -604,23 +622,23 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -628,7 +646,10 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From e6a012f134651744b941058aa37740cbfc9c7c12 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 18 Jun 2025 10:55:06 +0200 Subject: [PATCH 195/200] Changed go dockerfile to 1.24 --- functions/onprem/worker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/onprem/worker/Dockerfile b/functions/onprem/worker/Dockerfile index a2fb13b3..e25112d0 100755 --- a/functions/onprem/worker/Dockerfile +++ b/functions/onprem/worker/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.23 as builder +FROM golang:1.24 as builder WORKDIR /app #RUN go get github.com/docker/docker/api/types github.com/docker/docker/api/types/container github.com/docker/docker/client From 0c9931a70fe51682fb4db9e94377a4dc164097c6 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 25 Jun 2025 22:05:42 +0200 Subject: [PATCH 196/200] Minor environment / org_id name changes --- .env | 1 - backend/go-app/main.go | 4 ++-- backend/go-app/walkoff.go | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 53204db2..0b1e8cee 100755 --- a/.env +++ b/.env @@ -1,5 +1,4 @@ # Default execution environment for workers -ORG_ID=Shuffle ENVIRONMENT_NAME=Shuffle # Sanitize liquid.py input diff --git a/backend/go-app/main.go b/backend/go-app/main.go index cfbc768a..bd939374 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -638,7 +638,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { } else { log.Printf("[DEBUG] Successfully created the default org!") - defaultEnv := os.Getenv("ORG_ID") + defaultEnv := os.Getenv("ENVIRONMENT_NAME") if len(defaultEnv) == 0 { defaultEnv = "Shuffle" log.Printf("[DEBUG] Setting default environment for org to %s", defaultEnv) @@ -3925,7 +3925,7 @@ func runInitEs(ctx context.Context) { log.Printf("[INFO] Running with HTTPS proxy %s (env: HTTPS_PROXY)", httpsProxy) } - defaultEnv := os.Getenv("ORG_ID") + defaultEnv := os.Getenv("ENVIRONMENT_NAME") if len(defaultEnv) == 0 { defaultEnv = "Shuffle" log.Printf("[DEBUG] Setting default environment for org to %s", defaultEnv) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 2331a273..c194d091 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -292,6 +292,8 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { log.Printf("[WARNING] Failed handling Orborus failover: %s", err) } + log.Printf("[DEBUG] Issue with environment ID: %s", orgId) + return } From 4f48d712521b867dac2dc4e8f80fb2e501b0ec62 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 26 Jun 2025 00:24:50 +0200 Subject: [PATCH 197/200] Removed more old files --- functions/extensions/aws-s3-lambda/README.md | 2 - .../extensions/aws-s3-lambda/s3_function.py | 22 - .../Shuffle/requirements.txt | 2 - .../cortex-responders/Shuffle/shuffle.json | 43 -- .../cortex-responders/Shuffle/shuffle.py | 27 - .../Shuffle_Webhook/requirements.txt | 2 - .../Shuffle_Webhook/shuffle_webhook.json | 28 - .../Shuffle_Webhook/shuffle_webhook.py | 28 - functions/extensions/elasticsearch/run.sh | 19 - functions/extensions/k8s/shuffle/.helmignore | 23 - functions/extensions/k8s/shuffle/Chart.yaml | 24 - .../k8s/shuffle/templates/NOTES.txt | 22 - .../k8s/shuffle/templates/_helpers.tpl | 62 -- .../k8s/shuffle/templates/deployment.yaml | 253 -------- .../extensions/k8s/shuffle/templates/hpa.yaml | 28 - .../k8s/shuffle/templates/ingress.yaml | 61 -- .../shuffle/templates/persistent-volume.yaml | 24 - .../templates/persistentvolumeclaim.yaml | 17 - .../k8s/shuffle/templates/service.yaml | 52 -- .../k8s/shuffle/templates/serviceaccount.yaml | 12 - .../templates/tests/test-connection.yaml | 15 - functions/extensions/k8s/shuffle/values.yaml | 82 --- functions/extensions/misp/Dockerfile | 23 - functions/extensions/misp/docker-compose.yml | 9 - functions/extensions/misp/requirements.txt | 2 - functions/extensions/misp/sub.py | 57 -- .../extensions/scripts/disable_disk_check.sh | 11 - functions/extensions/swarm/.gitignore | 1 - functions/extensions/swarm/docker-compose.yml | 131 ----- functions/extensions/swarm/network.sh | 1 - functions/extensions/swarm/orborus.yml | 39 -- functions/extensions/swarm/run.sh | 4 - functions/extensions/swarm/run_orborus.sh | 4 - functions/extensions/swarm/shuffle-apps/tmp | 0 .../extensions/swarm/shuffle-database/tmp | 0 functions/extensions/swarm/shuffle-files/tmp | 0 functions/extensions/swarm/stop.sh | 3 - functions/extensions/wazuh/custom-shuffle | 36 -- functions/extensions/wazuh/custom-shuffle.py | 206 ------- functions/extensions/wazuh/ossec.conf | 6 - functions/usecases/README.md | 16 - functions/usecases/categories.json | 260 -------- functions/usecases/categories.rtf | 555 ------------------ functions/usecases/read_categories.py | 66 --- 44 files changed, 2278 deletions(-) delete mode 100644 functions/extensions/aws-s3-lambda/README.md delete mode 100644 functions/extensions/aws-s3-lambda/s3_function.py delete mode 100755 functions/extensions/cortex-responders/Shuffle/requirements.txt delete mode 100755 functions/extensions/cortex-responders/Shuffle/shuffle.json delete mode 100755 functions/extensions/cortex-responders/Shuffle/shuffle.py delete mode 100755 functions/extensions/cortex-responders/Shuffle_Webhook/requirements.txt delete mode 100755 functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.json delete mode 100755 functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.py delete mode 100755 functions/extensions/elasticsearch/run.sh delete mode 100755 functions/extensions/k8s/shuffle/.helmignore delete mode 100755 functions/extensions/k8s/shuffle/Chart.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/NOTES.txt delete mode 100755 functions/extensions/k8s/shuffle/templates/_helpers.tpl delete mode 100755 functions/extensions/k8s/shuffle/templates/deployment.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/hpa.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/ingress.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/persistent-volume.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/service.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/serviceaccount.yaml delete mode 100755 functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml delete mode 100755 functions/extensions/k8s/shuffle/values.yaml delete mode 100755 functions/extensions/misp/Dockerfile delete mode 100755 functions/extensions/misp/docker-compose.yml delete mode 100755 functions/extensions/misp/requirements.txt delete mode 100755 functions/extensions/misp/sub.py delete mode 100755 functions/extensions/scripts/disable_disk_check.sh delete mode 100644 functions/extensions/swarm/.gitignore delete mode 100644 functions/extensions/swarm/docker-compose.yml delete mode 100644 functions/extensions/swarm/network.sh delete mode 100644 functions/extensions/swarm/orborus.yml delete mode 100644 functions/extensions/swarm/run.sh delete mode 100644 functions/extensions/swarm/run_orborus.sh delete mode 100644 functions/extensions/swarm/shuffle-apps/tmp delete mode 100644 functions/extensions/swarm/shuffle-database/tmp delete mode 100644 functions/extensions/swarm/shuffle-files/tmp delete mode 100644 functions/extensions/swarm/stop.sh delete mode 100755 functions/extensions/wazuh/custom-shuffle delete mode 100755 functions/extensions/wazuh/custom-shuffle.py delete mode 100755 functions/extensions/wazuh/ossec.conf delete mode 100644 functions/usecases/README.md delete mode 100644 functions/usecases/categories.json delete mode 100644 functions/usecases/categories.rtf delete mode 100644 functions/usecases/read_categories.py diff --git a/functions/extensions/aws-s3-lambda/README.md b/functions/extensions/aws-s3-lambda/README.md deleted file mode 100644 index f665851a..00000000 --- a/functions/extensions/aws-s3-lambda/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# AWS Lambda forwarder to Shuffle -This function is made to forward S3 notifications to Shuffle to run a workflow when an object is made or updated. diff --git a/functions/extensions/aws-s3-lambda/s3_function.py b/functions/extensions/aws-s3-lambda/s3_function.py deleted file mode 100644 index d83d6154..00000000 --- a/functions/extensions/aws-s3-lambda/s3_function.py +++ /dev/null @@ -1,22 +0,0 @@ -import json -import urllib.parse -import urllib3 -import os - -print('Loading function') - -def lambda_handler(event, context): - # Get the object from the event and show its content type - bucket = event['Records'][0]['s3']['bucket']['name'] - - webhook = os.environ.get("SHUFFLE_WEBHOOK") - if not webhook: - return "No webhook environment defined: SHUFFLE_WEBHOOK" - - http = urllib3.PoolManager() - ret = http.request('POST', webhook, body=json.dumps(event["Records"][0]).encode("utf-8")) - if ret.status != 200: - return "Bad status code for webhook: %d" % ret.status - - print("Status code: %d\nData: %s" % (ret.status, ret.data)) - return "Successfully started with data %s" % ret.data diff --git a/functions/extensions/cortex-responders/Shuffle/requirements.txt b/functions/extensions/cortex-responders/Shuffle/requirements.txt deleted file mode 100755 index 6aabc3cf..00000000 --- a/functions/extensions/cortex-responders/Shuffle/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -cortexutils -requests diff --git a/functions/extensions/cortex-responders/Shuffle/shuffle.json b/functions/extensions/cortex-responders/Shuffle/shuffle.json deleted file mode 100755 index db755b49..00000000 --- a/functions/extensions/cortex-responders/Shuffle/shuffle.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "Shuffle", - "version": "1.0", - "author": "@frikkylikeme", - "url": "https://github.com/frikky/shuffle", - "license": "AGPL-V3", - "description": "Execute a workflow in Shuffle", - "dataTypeList": ["thehive:case", "thehive:alert", "thehive:case_artifact"], - "command": "Shuffle/shuffle.py", - "baseConfig": "Shuffle", - "configurationItems": [ - { - "name": "url", - "description": "The URL to your shuffle instance", - "type": "string", - "multi": false, - "required": true, - "defaultValue": "https://shuffler.io" - }, - { - "name": "api_key", - "description": "The API key to your Shuffle user", - "type": "string", - "multi": false, - "required": true - }, - { - "name": "verifyssl", - "description": "Verify SSL certificate", - "type": "boolean", - "multi": false, - "required": true, - "defaultValue": true - }, - { - "name": "workflow_id", - "description": "The ID of the workflow to execute", - "type": "string", - "multi": false, - "required": true - } - ] -} diff --git a/functions/extensions/cortex-responders/Shuffle/shuffle.py b/functions/extensions/cortex-responders/Shuffle/shuffle.py deleted file mode 100755 index db343230..00000000 --- a/functions/extensions/cortex-responders/Shuffle/shuffle.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -#encoding: utf-8 - -from cortexutils.responder import Responder -import requests - -class Shuffle(Responder): - def __init__(self): - Responder.__init__(self) - self.api_key = self.get_param("config.api_key", "") - self.url = self.get_param("config.url", "") - self.workflow_id = self.get_param("config.workflow_id", "") - self.verify = self.get_param('config.verifyssl', True, None) - - def run(self): - Responder.run(self) - parsed_url = "%s/api/v1/workflows/%s/execute" % (self.url, self.workflow_id) - headers = { - "Authorization": "Bearer %s" % self.api_key, - "User-Agent": "Cortex-Analyzer" - } - requests.post(parsed_url, headers=headers,verify=self.verify) - - self.report({'message': 'message sent'}) - -if __name__ == '__main__': - Shuffle().run() diff --git a/functions/extensions/cortex-responders/Shuffle_Webhook/requirements.txt b/functions/extensions/cortex-responders/Shuffle_Webhook/requirements.txt deleted file mode 100755 index 6aabc3cf..00000000 --- a/functions/extensions/cortex-responders/Shuffle_Webhook/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -cortexutils -requests diff --git a/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.json b/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.json deleted file mode 100755 index a6d3e9d8..00000000 --- a/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "Shuffle_webhook", - "version": "1.0", - "author": "@azgaviperr", - "url": "https://github.com/frikky/shuffle", - "license": "AGPL-V3", - "description": "Execute a webhook in Shuffle", - "dataTypeList": ["thehive:case", "thehive:alert", "thehive:case_artifact"], - "command": "Shuffle_Webhook/shuffle_webhook.py", - "baseConfig": "Shuffle_Webhook", - "configurationItems": [ - { - "name": "webhook_url", - "description": "The URL to your shuffle instance", - "type": "string", - "multi": false, - "required": true - }, - { - "name": "verifyssl", - "description": "Verify SSL certificate", - "type": "boolean", - "multi": false, - "required": true, - "defaultValue": true - } - ] -} diff --git a/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.py b/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.py deleted file mode 100755 index daed501c..00000000 --- a/functions/extensions/cortex-responders/Shuffle_Webhook/shuffle_webhook.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 -#encoding: utf-8 - -from cortexutils.responder import Responder -import requests - -class Shuffle(Responder): - def __init__(self): - Responder.__init__(self) - self.api_key = self.get_param("config.api_key", "") - self.webhook_url = self.get_param("config.webhook_url", "") - self.webhook_id = self.get_param("config.webhook_id", "") - self.verify = self.get_param('config.verifyssl', True, None) - self.data = self.get_param('data') - - def run(self): - Responder.run(self) - headers = { - "Content-Type": "application/json", - "Accept": "application/json", - "User-Agent": "Cortex-Analyzer" - } - requests.post(self.webhook_url, headers=headers,verify=self.verify, json=self.data) - - self.report({'message': 'message sent'}) - -if __name__ == '__main__': - Shuffle().run() diff --git a/functions/extensions/elasticsearch/run.sh b/functions/extensions/elasticsearch/run.sh deleted file mode 100755 index 666b1d95..00000000 --- a/functions/extensions/elasticsearch/run.sh +++ /dev/null @@ -1,19 +0,0 @@ -#docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ELASTICSEARCH_USERNAME=frikky -e ELASTICSEARCH_PASSWORD=likeme -e xpack.security.enabled=true docker.elastic.co/elasticsearch/elasticsearch:7.12.1 -#docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.12.1 -# -# -# -#echo "\nWaiting for 1.5 minute, then adding data" -#sleep 90 -#echo "\nSlept 90 seconds: ADDING DATA" -#curl -XPOST http://localhost:9200/_security/user/frikky -H "Content-Type: application/json" -d '{"enabled": true, "email": "frikky@shuffler.io"}' - -#curl -XPOST -u frikky:likeme http://localhost:9200/samples/_doc -H "Content-Type: application/json" -d '{"src": "122.14.137.67", "dst": "103.35.191.16", "message": "alert", "md5": "CAEF973033E593C625FB2AA34F7026DC", "sha256": "DB1AEC5222075800EDA75D7205267569679B424E5C58A28102417F46D3B5790D", "hits": 0}' -#echo -#curl -XPOST -u frikky:likeme http://localhost:9200/samples/_doc -H "Content-Type: application/json" -d '{"src": "134.119.219.71", "dst": "103.35.191.41", "message": "alert", "md5": "9498FF82A64FF445398C8426ED63EA5B", "sha256": "8B2E701E91101955C73865589A4C72999AEABC11043F712E05FDB1C17C4AB19A", "hits": 0}' -# -#echo -#curl -XPOST -u frikky:likeme http://localhost:9200/samples2/_doc -H "Content-Type: application/json" -d '{"src": "122.14.137.67", "dst": "103.35.191.16", "message": "alert", "md5": "CAEF973033E593C625FB2AA34F7026DC", "sha256": "DB1AEC5222075800EDA75D7205267569679B424E5C58A28102417F46D3B5790D"}' -#echo -#curl -XPOST -u frikky:likeme http://localhost:9200/samples2/_doc -H "Content-Type: application/json" -d '{"src": "134.119.219.71", "dst": "103.35.191.41", "message": "alert", "md5": "9498FF82A64FF445398C8426ED63EA5B", "sha256": "8B2E701E91101955C73865589A4C72999AEABC11043F712E05FDB1C17C4AB19A"}' -#echo diff --git a/functions/extensions/k8s/shuffle/.helmignore b/functions/extensions/k8s/shuffle/.helmignore deleted file mode 100755 index 0e8a0eb3..00000000 --- a/functions/extensions/k8s/shuffle/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/functions/extensions/k8s/shuffle/Chart.yaml b/functions/extensions/k8s/shuffle/Chart.yaml deleted file mode 100755 index eb53a0b5..00000000 --- a/functions/extensions/k8s/shuffle/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: shuffle -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.2.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "2.0.0" diff --git a/functions/extensions/k8s/shuffle/templates/NOTES.txt b/functions/extensions/k8s/shuffle/templates/NOTES.txt deleted file mode 100755 index eaba4460..00000000 --- a/functions/extensions/k8s/shuffle/templates/NOTES.txt +++ /dev/null @@ -1,22 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "shuffle.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "shuffle.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "shuffle.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "shuffle.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT -{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/_helpers.tpl b/functions/extensions/k8s/shuffle/templates/_helpers.tpl deleted file mode 100755 index 66efef07..00000000 --- a/functions/extensions/k8s/shuffle/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "shuffle.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "shuffle.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "shuffle.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "shuffle.labels" -}} -helm.sh/chart: {{ include "shuffle.chart" . }} -{{ include "shuffle.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "shuffle.selectorLabels" -}} -app.kubernetes.io/name: {{ include "shuffle.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "shuffle.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "shuffle.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/deployment.yaml b/functions/extensions/k8s/shuffle/templates/deployment.yaml deleted file mode 100755 index f2050d82..00000000 --- a/functions/extensions/k8s/shuffle/templates/deployment.yaml +++ /dev/null @@ -1,253 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Values.name }}frontend - namespace: {{ .Values.namespace | quote }} -spec: - replicas: 1 - selector: - matchLabels: - service: shuffle - app: shuffle-frontend - template: - metadata: - labels: - service: shuffle - app: shuffle-frontend - spec: - containers: - - name: shuffle-frontend - image: ghcr.io/frikky/shuffle-frontend:nightly - imagePullPolicy: {{ .Values.image.pullPolicy | quote }} - env: - - name: BACKEND_HOSTNAME - value: backend-service - - name: TZ - value: Asia/Shanghai - ports: - - name: http - containerPort: 80 - hostPort: 3001 - - name: https - containerPort: 443 - hostname: shuffle-frontend - restartPolicy: Always - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Values.name }}backend - namespace: {{ .Values.namespace | quote }} -spec: - replicas: 1 - selector: - matchLabels: - service: shuffle - app: shuffle-backend - template: - metadata: - labels: - service: shuffle - app: shuffle-backend - spec: - containers: - - name: shuffle-backend - image: ghcr.io/frikky/shuffle-backend:nightly - env: - - name: BACKEND_HOSTNAME - value: "backend-service" - - name: BACKEND_PORT - value: "5001" - - name: ENVIRONMENT_NAME - value: "Shuffle" - - name: HTTPS_PROXY - - name: HTTP_PROXY - - name: ORG_ID - value: "Shuffle" - - name: OUTER_HOSTNAME - value: "backend-service" - - name: SHUFFLE_APP_FORCE_UPDATE - value: "false" - - name: SHUFFLE_APP_HOTLOAD_FOLDER - value: "/shuffle-apps" - - name: SHUFFLE_APP_HOTLOAD_LOCATION - value: "/shuffle-apps" - - name: SHUFFLE_CONTAINER_AUTO_CLEANUP - value: "false" - - name: SHUFFLE_DEFAULT_APIKEY - - name: SHUFFLE_DEFAULT_PASSWORD - - name: SHUFFLE_DEFAULT_USERNAME - - name: SHUFFLE_DOWNLOAD_AUTH_BRANCH - - name: SHUFFLE_DOWNLOAD_AUTH_PASSWORD - - name: SHUFFLE_DOWNLOAD_AUTH_USERNAME - - name: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH - - name: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION - - name: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD - - name: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME - - name: SHUFFLE_ELASTIC - value: "true" - - name: SHUFFLE_OPENSEARCH_APIKEY - - name: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE - - name: SHUFFLE_OPENSEARCH_CLOUDID - - name: SHUFFLE_OPENSEARCH_PASSWORD - - name: SHUFFLE_OPENSEARCH_PROXY - - name: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY - value: "true" - - name: SHUFFLE_OPENSEARCH_URL - value: http://opensearch-service:9200 - - name: SHUFFLE_OPENSEARCH_USERNAME - value: "" - - name: SHUFFLE_PASS_APP_PROXY - value: "FALSE" - - name: SHUFFLE_PASS_WORKER_PROXY - value: "FALSE" - volumeMounts: - - mountPath: /var/run/docker.sock - name: docker-sock - - mountPath: /shuffle-apps - name: shuffle-app-hotload-location - - mountPath: /shuffle-files - name: shuffle-file-location - hostname: shuffle-backend - volumes: - - name: docker-sock - hostPath: - path: /var/run/docker.sock - - name: shuffle-app-hotload-location - hostPath: - path: /data/kubernetes/shuffle-apps - type: DirectoryOrCreate - - name: shuffle-file-location - hostPath: - path: /data/kubernetes/shuffle-files - type: DirectoryOrCreate - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Values.name }}orborus - namespace: {{ .Values.namespace | quote }} -spec: - replicas: 1 - selector: - matchLabels: - service: shuffle - app: shuffle-orborus - template: - metadata: - labels: - service: shuffle - app: shuffle-orborus - spec: - containers: - - name: shuffle-orborus - image: ghcr.io/frikky/shuffle-orborus:nightly - env: - - name: RUNNING_MODE - value: kubernetes - - name: BASE_URL - value: http://backend-service:5001 - - name: CLEANUP - value: "false" - - name: DOCKER_API_VERSION - value: "1.40" - - name: ENVIRONMENT_NAME - value: Shuffle - - name: HTTPS_PROXY - - name: HTTP_PROXY - - name: ORG_ID - value: Shuffle - - name: SHUFFLE_APP_SDK_VERSION - value: 0.8.97 - - name: SHUFFLE_BASE_IMAGE_NAME - value: frikky - - name: SHUFFLE_BASE_IMAGE_REGISTRY - value: ghcr.io - - name: SHUFFLE_BASE_IMAGE_TAG_SUFFIX - value: "-0.8.80" - - name: SHUFFLE_ORBORUS_EXECUTION_TIMEOUT - value: "600" - - name: SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY - value: "50" - - name: SHUFFLE_PASS_WORKER_PROXY - value: "TRUE" - - name: SHUFFLE_WORKER_VERSION - value: nightly - - name: TZ - value: Asia/Shanghai - volumeMounts: - - mountPath: /var/run/docker.sock - name: docker-sock - hostname: shuffle-orborus - volumes: - - name: docker-sock - hostPath: - path: /var/run/docker.sock -#--- -#apiVersion: apps/v1 -#kind: Deployment -#metadata: -# name: {{ .Values.name }}opensearch -# namespace: {{ .Values.namespace | quote }} -#spec: -# replicas: 1 -# selector: -# matchLabels: -# service: shuffle -# app: shuffle-opensearch -# template: -# metadata: -# labels: -# service: shuffle -# app: shuffle-opensearch -# spec: -# nodeSelector: -# node.bdlab-venus.com/opensearch: available -# initContainers: -# - name: permissions-fix -# image: frikky/busybox -# #volumeMounts: -# # - name: opensearch-claim0 -# # mountPath: /usr/share/elasticsearch/data -# command: [ 'chown' ] -# args: [ '1000:1000', '/usr/share/elasticsearch/data' ] -# containers: -# - name: shuffle-opensearch -# image: opensearchproject/opensearch:1.0.1 -# env: -# - name: TZ -# value: Asia/Shanghai -# - name: bootstrap.memory_lock -# value: "false" -# - name: OPENSEARCH_JAVA_OPTS -# value: "-Xms1024m -Xmx1024m" -# - name: opendistro_security.disabled -# value: "true" -# - name: cluster.routing.allocation.disk.threshold_enabled -# value: "false" -# - name: cluster.name -# value: shuffle-cluster -# - name: node.name -# value: opensearch-service -# - name: discovery.seed_hosts -# value: opensearch-service -# - name: cluster.initial_master_nodes -# value: opensearch-service - # volumeMounts: - # - mountPath: /usr/share/opensearch/data - # name: opensearch-claim0 - #volumes: - # - name: opensearch-claim0 - # persistentVolumeClaim: - # claimName: opensearch-claim0 -# volumeMounts: -# - mountPath: /usr/share/opensearch/data -# readOnly: true -# name: db-location -# volumes: -# - name: db-location -# hostPath: -# path: /data/kubernetes/shuffle-opensearch -# type: DirectoryOrCreate diff --git a/functions/extensions/k8s/shuffle/templates/hpa.yaml b/functions/extensions/k8s/shuffle/templates/hpa.yaml deleted file mode 100755 index 49f84ca2..00000000 --- a/functions/extensions/k8s/shuffle/templates/hpa.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "shuffle.fullname" . }} - labels: - {{- include "shuffle.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "shuffle.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/ingress.yaml b/functions/extensions/k8s/shuffle/templates/ingress.yaml deleted file mode 100755 index e35d2ae0..00000000 --- a/functions/extensions/k8s/shuffle/templates/ingress.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "shuffle.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "shuffle.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml b/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml deleted file mode 100755 index 934c68e2..00000000 --- a/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml +++ /dev/null @@ -1,24 +0,0 @@ -#apiVersion: v1 -#kind: PersistentVolume -#metadata: -# name: opensearch-claim0 -# labels: -# app: opensearch-claim0 -#spec: -# capacity: -# storage: "10G" -# volumeMode: Filesystem -# persistentVolumeReclaimPolicy: Retain -# storageClassName: local-storage -# accessModes: -# - "ReadWriteOnce" -# local: -# path: "/data/kubernetes/shuffle-opensearch" -# nodeAffinity: -# required: -# nodeSelectorTerms: -# - matchExpressions: -# - key: node.dollar.com/opensearch -# operator: In -# values: -# - available diff --git a/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml b/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml deleted file mode 100755 index 7d1736a4..00000000 --- a/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml +++ /dev/null @@ -1,17 +0,0 @@ -#apiVersion: v1 -#kind: PersistentVolumeClaim -#metadata: -# name: opensearch-claim0 -# namespace: {{ .Values.namespace }} -# labels: -# app: opensearch-claim0 -#spec: -# selector: -# matchLabels: -# app: opensearch-claim0 -# accessModes: -# - ReadWriteOnce -# storageClassName: local-storage -# resources: -# requests: -# storage: 5Gi diff --git a/functions/extensions/k8s/shuffle/templates/service.yaml b/functions/extensions/k8s/shuffle/templates/service.yaml deleted file mode 100755 index 8fc78dfa..00000000 --- a/functions/extensions/k8s/shuffle/templates/service.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: backend-service - namespace: {{ .Values.namespace | quote }} -spec: - ports: - - name: "5001" - port: 5001 - targetPort: 5001 - selector: - app: shuffle-backend - ---- -apiVersion: v1 -kind: Service -metadata: - name: frontend-service - namespace: {{ .Values.namespace | quote }} -spec: - type: NodePort - externalTrafficPolicy: Local - ports: - - name: "3001" - port: 3001 - nodePort: 3001 - targetPort: 80 - - name: "3443" - port: 3443 - nodePort: 3443 - targetPort: 443 - protocol: TCP - selector: - app: shuffle-frontend - -#--- -#apiVersion: v1 -#kind: Service -#metadata: -# name: opensearch-service -# namespace: {{ .Values.namespace | quote }} -#spec: -# type: NodePort -# externalTrafficPolicy: Local -# ports: -# - name: "9200" -# port: 9200 -# targetPort: 9200 -# nodePort: 9200 -# protocol: TCP -# selector: -# app: shuffle-opensearch diff --git a/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml b/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml deleted file mode 100755 index 11e9bdb7..00000000 --- a/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "shuffle.serviceAccountName" . }} - labels: - {{- include "shuffle.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml b/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml deleted file mode 100755 index e68f3142..00000000 --- a/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "shuffle.fullname" . }}-test-connection" - labels: - {{- include "shuffle.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "shuffle.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/functions/extensions/k8s/shuffle/values.yaml b/functions/extensions/k8s/shuffle/values.yaml deleted file mode 100755 index 07e935c6..00000000 --- a/functions/extensions/k8s/shuffle/values.yaml +++ /dev/null @@ -1,82 +0,0 @@ -# Default values for shuffle. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/functions/extensions/misp/Dockerfile b/functions/extensions/misp/Dockerfile deleted file mode 100755 index f2b8ae3f..00000000 --- a/functions/extensions/misp/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM python:3.9.4-alpine as base - -FROM base as builder - -RUN mkdir /install -WORKDIR /install - -FROM base -RUN apk add g++ - -COPY --from=builder /install /usr/local -COPY requirements.txt /requirements.txt -RUN pip3 install -r /requirements.txt - - -RUN mkdir /app -WORKDIR /app -COPY requirements.txt /app/requirements.txt -RUN python3 -m pip install -r /app/requirements.txt - -COPY sub.py /app/sub.py - -CMD ["python3", "sub.py"] diff --git a/functions/extensions/misp/docker-compose.yml b/functions/extensions/misp/docker-compose.yml deleted file mode 100755 index 5b0130c0..00000000 --- a/functions/extensions/misp/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3' -services: - zmq: - image: ghcr.io/frikky/shuffle-zmq:latest - environment: - - ZMQ_HOSTNAME=localhost - - ZMQ_PORT=50000 - - ZMQ_FORWARD_URL=https://shuffler.io/api/v1/hooks/webhook_e09bea36-9976-1421-82bc-b8764ca83c1e - restart: unless-stopped diff --git a/functions/extensions/misp/requirements.txt b/functions/extensions/misp/requirements.txt deleted file mode 100755 index a1cdf7dd..00000000 --- a/functions/extensions/misp/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pyzmq -requests diff --git a/functions/extensions/misp/sub.py b/functions/extensions/misp/sub.py deleted file mode 100755 index feb78c08..00000000 --- a/functions/extensions/misp/sub.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -print("Running imports") -import sys -import zmq -import json -import time -import pprint -import os -import sys -import requests - -forward_url = os.getenv("ZMQ_FORWARD_URL", "") -print("Checking forward url (ZMQ_FORWARD_URL): %s" % forward_url) -def handle_hook(data): - ret = requests.post(forward_url, json=data) - print(ret.text) - print(ret.status_code) - -def main(): - host = os.getenv("ZMQ_HOST", "localhost") - port = os.getenv("ZMQ_PORT", "50000") - - if len(forward_url) == 0: - print("Failed to start - define ZMQ_FORWARD_URL for webhook forwarder") - exit(0) - - print("Starting connection setup to %s:%s" % (host, port)) - context = zmq.Context() - socket = context.socket(zmq.SUB) - socket.connect ("tcp://%s:%s" % (host, port)) - socket.setsockopt(zmq.SUBSCRIBE, b'') - - poller = zmq.Poller() - poller.register(socket, zmq.POLLIN) - - print("Starting zmq check for %s:%s" % (host, port)) - while True: - socks = dict(poller.poll(timeout=None)) - if socket in socks and socks[socket] == zmq.POLLIN: - message = socket.recv() - #print(message) - topic, s, m = message.decode('utf-8').partition(" ") - - d = json.loads(m) - try: - # print test if you want status (heartbeat) - test = d["status"] - except KeyError: - handle_hook(d) - - time.sleep(1) - -if __name__ == "__main__": - print("In init ") - main() diff --git a/functions/extensions/scripts/disable_disk_check.sh b/functions/extensions/scripts/disable_disk_check.sh deleted file mode 100755 index cadda899..00000000 --- a/functions/extensions/scripts/disable_disk_check.sh +++ /dev/null @@ -1,11 +0,0 @@ -curl -XPUT -u admin:admin https://localhost:9200/_cluster/settings -H "Content-Type:application/json" -k -d \ -'{ - "transient": { - "cluster.routing.allocation.disk.threshold_enabled": false - } -}' - -curl -XPUT -u admin:admin https://localhost:9200/_all/_settings -H "Content-Type: application/json" -k -d \ -'{ - "index.blocks.read_only_allow_delete": null -}' diff --git a/functions/extensions/swarm/.gitignore b/functions/extensions/swarm/.gitignore deleted file mode 100644 index 9ce4a537..00000000 --- a/functions/extensions/swarm/.gitignore +++ /dev/null @@ -1 +0,0 @@ -shuffle-database/nodes diff --git a/functions/extensions/swarm/docker-compose.yml b/functions/extensions/swarm/docker-compose.yml deleted file mode 100644 index 57ea9b49..00000000 --- a/functions/extensions/swarm/docker-compose.yml +++ /dev/null @@ -1,131 +0,0 @@ -version: '3.4' -services: - backend: - image: ghcr.io/frikky/shuffle-backend:nightly - #hostname: shuffle-backend - environment: - BACKEND_HOSTNAME: backend - OUTER_HOSTNAME: backend - BACKEND_PORT: '5001' - HTTPS_PROXY: '' - HTTP_PROXY: '' - SHUFFLE_APP_DOWNLOAD_LOCATION: https://github.com/frikky/shuffle-apps - SHUFFLE_APP_FORCE_UPDATE: 'false' - SHUFFLE_APP_HOTLOAD_FOLDER: /shuffle-apps - SHUFFLE_APP_HOTLOAD_LOCATION: ./shuffle-apps - DATASTORE_EMULATOR_HOST: "shuffle-database:8000" - DOCKER_API_VERSION: '1.40' - SHUFFLE_BASE_IMAGE_NAME: frikky - SHUFFLE_BASE_IMAGE_REGISTRY: ghcr.io - SHUFFLE_BASE_IMAGE_TAG_SUFFIX: '-0.9.30' - SHUFFLE_CONTAINER_AUTO_CLEANUP: 'true' - SHUFFLE_DEFAULT_APIKEY: '' - SHUFFLE_FILE_LOCATION: /shuffle-files - SHUFFLE_OPENSEARCH_APIKEY: '' - SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: '' - SHUFFLE_OPENSEARCH_CLOUDID: '' - SHUFFLE_OPENSEARCH_PROXY: '' - SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: 'true' - SHUFFLE_OPENSEARCH_URL: http://opensearch:9200 - SHUFFLE_PASS_APP_PROXY: 'FALSE' - SHUFFLE_PASS_WORKER_PROXY: 'TRUE' - SHUFFLE_ELASTIC: 'true' - #SHUFFLE_ENCRYPTION_MODIFIER: - ports: - - "5001:5001" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./shuffle-apps:/shuffle-apps - - ./shuffle-files:/shuffle-files - networks: - - shuffle_prod - #- reverseproxy - depends_on: - - opensearch - logging: - driver: json-file - frontend: - image: ghcr.io/frikky/shuffle-frontend:nightly - healthcheck: - test: curl -fs http://localhost:80 || exit 1 - interval: 30s - timeout: 5s - retries: 3 - ports: - - "3001:80" - - "3443:443" - networks: - - shuffle_prod - #- reverseproxy - environment: - - "BACKEND_HOSTNAME=backend" - depends_on: - - backend - deploy: - update_config: - order: start-first - opensearch: - image: opensearchproject/opensearch:1.1.0 - healthcheck: - test: curl -fs http://localhost:9200/_cat/health || exit 1 - interval: 30s - timeout: 5s - retries: 3 - environment: - - bootstrap.memory_lock=false - - "OPENSEARCH_JAVA_OPTS=-Xms1024m -Xmx1024m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM - - plugins.security.disabled=true - - cluster.routing.allocation.disk.threshold_enabled=false - - cluster.name=shuffle-cluster - - node.name=opensearch - - discovery.seed_hosts=opensearch - - cluster.initial_master_nodes=opensearch - - node.store.allow_mmap=false - volumes: - - ./shuffle-database:/usr/share/opensearch/data:rw - networks: - - shuffle_prod - #- reverseproxy - logging: - driver: json-file - - orborus: - image: ghcr.io/frikky/shuffle-orborus:nightly - #hostname: shuffle-orborus - environment: - #SHUFFLE_WORKER_VERSION: nightly - SHUFFLE_APP_SDK_VERSION: 0.8.97 - SHUFFLE_WORKER_VERSION: nightly - BASE_URL: http://backend:5001 - #BASE_URL: http://192.168.86.37:5001 - CLEANUP: 'true' - DOCKER_API_VERSION: '1.40' - ENVIRONMENT_NAME: Shuffle - HTTPS_PROXY: '' - HTTP_PROXY: '' - ORG_ID: Shuffle - SHUFFLE_BASE_IMAGE_NAME: frikky - SHUFFLE_BASE_IMAGE_REGISTRY: ghcr.io - SHUFFLE_BASE_IMAGE_TAG_SUFFIX: -0.8.80 - SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY: '50' - SHUFFLE_ORBORUS_EXECUTION_TIMEOUT: '800' - SHUFFLE_PASS_APP_PROXY: 'FALSE' - SHUFFLE_PASS_WORKER_PROXY: 'TRUE' - SHUFFLE_SCALE_REPLICAS: 5 - SHUFFLE_SWARM_NETWORK_NAME: shuffle_prod - SHUFFLE_SWARM_CONFIG: "run" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - networks: - - shuffle_prod - #- reverseproxy - logging: - driver: json-file - -networks: - shuffle_prod: - driver: overlay - external: true - #reverseproxy: - # driver: overlay - # #external: true diff --git a/functions/extensions/swarm/network.sh b/functions/extensions/swarm/network.sh deleted file mode 100644 index f3a323ed..00000000 --- a/functions/extensions/swarm/network.sh +++ /dev/null @@ -1 +0,0 @@ -docker network create -d overlay shuffle_prod diff --git a/functions/extensions/swarm/orborus.yml b/functions/extensions/swarm/orborus.yml deleted file mode 100644 index fb90b10f..00000000 --- a/functions/extensions/swarm/orborus.yml +++ /dev/null @@ -1,39 +0,0 @@ -version: '3.4' -services: - orborus: - image: ghcr.io/frikky/shuffle-orborus:nightly - #hostname: shuffle-orborus - environment: - #SHUFFLE_WORKER_VERSION: nightly - SHUFFLE_APP_SDK_VERSION: 0.8.97 - SHUFFLE_WORKER_VERSION: nightly - BASE_URL: http://:5001 - #BASE_URL: http://192.168.86.37:5001 - CLEANUP: 'true' - DOCKER_API_VERSION: '1.40' - ENVIRONMENT_NAME: Shuffle - HTTPS_PROXY: '' - HTTP_PROXY: '' - ORG_ID: Shuffle - SHUFFLE_BASE_IMAGE_NAME: frikky - SHUFFLE_BASE_IMAGE_REGISTRY: ghcr.io - SHUFFLE_BASE_IMAGE_TAG_SUFFIX: -0.8.80 - SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY: '50' - SHUFFLE_ORBORUS_EXECUTION_TIMEOUT: '800' - SHUFFLE_PASS_APP_PROXY: 'FALSE' - SHUFFLE_PASS_WORKER_PROXY: 'TRUE' - SHUFFLE_SCALE_REPLICAS: 5 - SHUFFLE_SWARM_NETWORK_NAME: shuffle_prod - SHUFFLE_SWARM_CONFIG: "run" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - networks: - - shuffle_prod - #- reverseproxy - logging: - driver: json-file - -networks: - shuffle_prod: - driver: overlay - external: true diff --git a/functions/extensions/swarm/run.sh b/functions/extensions/swarm/run.sh deleted file mode 100644 index faeadab8..00000000 --- a/functions/extensions/swarm/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -docker swarm init -chown 1000:1000 -R shuffle-database/ -docker network create -d overlay shuffle_prod -docker stack deploy --compose-file=docker-compose.yml shuffle_swarm diff --git a/functions/extensions/swarm/run_orborus.sh b/functions/extensions/swarm/run_orborus.sh deleted file mode 100644 index bcbd626f..00000000 --- a/functions/extensions/swarm/run_orborus.sh +++ /dev/null @@ -1,4 +0,0 @@ -docker swarm init -chown 1000:1000 -R shuffle-database/ -docker network create -d overlay shuffle_prod -docker stack deploy --compose-file=orborus.yml shuffle_orborus diff --git a/functions/extensions/swarm/shuffle-apps/tmp b/functions/extensions/swarm/shuffle-apps/tmp deleted file mode 100644 index e69de29b..00000000 diff --git a/functions/extensions/swarm/shuffle-database/tmp b/functions/extensions/swarm/shuffle-database/tmp deleted file mode 100644 index e69de29b..00000000 diff --git a/functions/extensions/swarm/shuffle-files/tmp b/functions/extensions/swarm/shuffle-files/tmp deleted file mode 100644 index e69de29b..00000000 diff --git a/functions/extensions/swarm/stop.sh b/functions/extensions/swarm/stop.sh deleted file mode 100644 index c516f348..00000000 --- a/functions/extensions/swarm/stop.sh +++ /dev/null @@ -1,3 +0,0 @@ -docker stack rm shuffle_swarm - -# diff --git a/functions/extensions/wazuh/custom-shuffle b/functions/extensions/wazuh/custom-shuffle deleted file mode 100755 index bd540414..00000000 --- a/functions/extensions/wazuh/custom-shuffle +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh -# Created by Shuffle, AS. . - -WPYTHON_BIN="framework/python/bin/python3" - -SCRIPT_PATH_NAME="$0" - -DIR_NAME="$(cd $(dirname ${SCRIPT_PATH_NAME}); pwd -P)" -SCRIPT_NAME="$(basename ${SCRIPT_PATH_NAME})" - -case ${DIR_NAME} in - */active-response/bin | */wodles*) - if [ -z "${WAZUH_PATH}" ]; then - WAZUH_PATH="$(cd ${DIR_NAME}/../..; pwd)" - fi - - PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py" - ;; - */bin) - if [ -z "${WAZUH_PATH}" ]; then - WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)" - fi - - PYTHON_SCRIPT="${WAZUH_PATH}/framework/scripts/${SCRIPT_NAME}.py" - ;; - */integrations) - if [ -z "${WAZUH_PATH}" ]; then - WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)" - fi - - PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py" - ;; -esac - - -${WAZUH_PATH}/${WPYTHON_BIN} ${PYTHON_SCRIPT} "$@" diff --git a/functions/extensions/wazuh/custom-shuffle.py b/functions/extensions/wazuh/custom-shuffle.py deleted file mode 100755 index 8e555ea9..00000000 --- a/functions/extensions/wazuh/custom-shuffle.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env python3 -# Created by Shuffle, AS. . -# Based on the Slack integration using Webhooks - -import json -import sys -import time -import os - -try: - import requests - from requests.auth import HTTPBasicAuth -except Exception as e: - print("No module 'requests' found. Install: pip install requests") - sys.exit(1) - -# ADD THIS TO ossec.conf configuration: -# -# custom-shuffle -# http://:3001/api/v1/hooks/ -# 3 -# json -# - -# Global vars -debug_enabled = False -pwd = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) -json_alert = {} -now = time.strftime("%a %b %d %H:%M:%S %Z %Y") - -# Set paths -log_file = '{0}/logs/integrations.log'.format(pwd) - -try: - with open("/tmp/shuffle_start.txt", "w+") as tmp: - tmp.write("Script started") -except: - pass - - -def main(args): - debug("# Starting") - - # Read args - alert_file_location = args[1] - webhook = args[3] - - debug("# Webhook") - debug(webhook) - - debug("# File location") - debug(alert_file_location) - - # Load alert. Parse JSON object. - try: - with open(alert_file_location) as alert_file: - json_alert = json.load(alert_file) - except: - debug("# Alert file %s doesn't exist" % alert_file_location) - - debug("# Processing alert") - try: - debug(json_alert) - except Exception as e: - debug("Failed getting json_alert %s" % e) - sys.exit(1) - - debug("# Generating message") - msg = generate_msg(json_alert) - if isinstance(msg, str): - if len(msg) == 0: - return - debug(msg) - - debug("# Sending message") - - try: - with open("/tmp/shuffle_end.txt", "w+") as tmp: - tmp.write("Script done pre-msg sending") - except: - pass - - - send_msg(msg, webhook) - - -def debug(msg): - if debug_enabled: - msg = "{0}: {1}\n".format(now, msg) - print(msg) - f = open(log_file, "a") - f.write(msg) - f.close() - -# Skips container kills to stop self-recursion -def filter_msg(alert): - # These are things that recursively happen because Shuffle starts Docker containers - skip = ["87924", "87900", "87901", "87902", "87903", "87904", "86001", "86002", "86003", "87932", "80710", "87929", "87928", "5710"] - if alert["rule"]["id"] in skip: - return False - - #try: - # if "docker" in alert["rule"]["description"].lower() and " - #msg['text'] = alert.get('full_log') - #except: - # pass - #msg['title'] = alert['rule']['description'] if 'description' in alert['rule'] else "N/A" - - return True - -def generate_msg(alert): - if not filter_msg(alert): - print("Skipping rule %s" % alert["rule"]["id"]) - return "" - - level = alert['rule']['level'] - - if (level <= 4): - severity = 1 - elif (level >= 5 and level <= 7): - severity = 2 - else: - severity = 3 - - msg = {} - msg['severity'] = severity - msg['pretext'] = "WAZUH Alert" - msg['title'] = alert['rule']['description'] if 'description' in alert['rule'] else "N/A" - msg['text'] = alert.get('full_log') - msg['rule_id'] = alert["rule"]["id"] - msg['timestamp'] = alert["timestamp"] - msg['id'] = alert['id'] - msg["all_fields"] = alert - - #msg['fields'] = [] - # msg['fields'].append({ - # "title": "Agent", - # "value": "({0}) - {1}".format( - # alert['agent']['id'], - # alert['agent']['name'] - # ), - # }) - #if 'agentless' in alert: - # msg['fields'].append({ - # "title": "Agentless Host", - # "value": alert['agentless']['host'], - # }) - - #msg['fields'].append({"title": "Location", "value": alert['location']}) - #msg['fields'].append({ - # "title": "Rule ID", - # "value": "{0} _(Level {1})_".format(alert['rule']['id'], level), - #}) - - #attach = {'attachments': [msg]} - - return json.dumps(msg) - - -def send_msg(msg, url): - debug("# In send msg") - headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'} - res = requests.post(url, data=msg, headers=headers, verify=False) - debug("# After send msg: %s" % res) - - -if __name__ == "__main__": - try: - # Read arguments - bad_arguments = False - if len(sys.argv) >= 4: - msg = '{0} {1} {2} {3} {4}'.format( - now, - sys.argv[1], - sys.argv[2], - sys.argv[3], - sys.argv[4] if len(sys.argv) > 4 else '', - ) - #debug_enabled = (len(sys.argv) > 4 and sys.argv[4] == 'debug') - debug_enabled = True - else: - msg = '{0} Wrong arguments'.format(now) - bad_arguments = True - - # Logging the call - try: - f = open(log_file, 'a') - except: - f = open(log_file, 'w+') - f.write("") - f.close() - - f = open(log_file, 'a') - f.write(msg + '\n') - f.close() - - if bad_arguments: - debug("# Exiting: Bad arguments. Inputted: %s" % sys.argv) - sys.exit(1) - - # Main function - main(sys.argv) - - except Exception as e: - debug(str(e)) - raise diff --git a/functions/extensions/wazuh/ossec.conf b/functions/extensions/wazuh/ossec.conf deleted file mode 100755 index e87bc53d..00000000 --- a/functions/extensions/wazuh/ossec.conf +++ /dev/null @@ -1,6 +0,0 @@ - - custom-shuffle - 9 - http://:/api/v1/hooks/webhook_hookid - json - diff --git a/functions/usecases/README.md b/functions/usecases/README.md deleted file mode 100644 index 553034c2..00000000 --- a/functions/usecases/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Mindmap exporter -Shuffle has a mindmap for Workflow use-cases. These can be changed and exported, with the most important piece being that they're explorable and editable. This has and will come in handy for us as we build it into the product. - -https://www.mindmeister.com/map/2172644474 - -## Editing the Mindmap -There are a few categories. To edit them, click the small plus next to the branch you want to change. - -## Exporting the Mindmap -Click "Export as RTF" in the top left corner of the URL. Download it there. - -## Generating the Shuffle-comaptible mindmap -1. Move the rtf file here -2. Rename it categories.rtf -3. Run the read_categories.py file (python3 read_categories.py) -4. You now have a file called categories.json locally with all the categories in JSON format, ready to be used in graphs. diff --git a/functions/usecases/categories.json b/functions/usecases/categories.json deleted file mode 100644 index e438a380..00000000 --- a/functions/usecases/categories.json +++ /dev/null @@ -1,260 +0,0 @@ -[ - { - "name": "1. Collect & Distribute", - "color": "#c51152", - "list": [ - { - "name": "2-way Ticket synchronization", - "items": {} - }, - { - "name": "Email management", - "items": { - "name": "Release a quarantined message", - "items": {} - } - }, - { - "name": "EDR to ticket", - "items": { - "name": "Get host information", - "items": {} - } - }, - { - "name": "SIEM to ticket", - "items": {} - }, - { - "name": "ChatOps", - "items": {} - }, - { - "name": "Threat Intel received", - "items": {} - }, - { - "name": "Domain investigation with LetsEncrypt", - "items": {} - }, - { - "name": "Botnet tracker", - "items": {} - }, - { - "name": "Get running containers", - "items": {} - }, - { - "name": "Assign tickets", - "items": {} - }, - { - "name": "Firewall alerts", - "items": { - "name": "URL filtering", - "items": {} - } - }, - { - "name": "IDS/IPS alerts", - "items": { - "name": "Manage policies", - "items": {} - } - }, - { - "name": "Deduplicate information", - "items": {} - }, - { - "name": "Correlate information", - "items": {} - } - ] - }, - { - "name": "2. Enrich", - "color": "#f4c20d", - "list": [ - { - "name": "Internal Enrichment", - "items": { - "name": "...", - "items": {} - } - }, - { - "name": "External historical Enrichment", - "items": { - "name": "...", - "items": {} - } - }, - { - "name": "Realtime", - "items": { - "name": "Analyze screenshots", - "items": {} - } - }, - { - "name": "Ticketing webhook verification", - "items": {} - } - ] - }, - { - "name": "3. Detect", - "color": "#3cba54", - "list": [ - { - "name": "Search SIEM (Sigma)", - "items": { - "name": "Endpoint", - "items": {} - } - }, - { - "name": "Search EDR (OSQuery)", - "items": {} - }, - { - "name": "Search emails (Phish)", - "items": { - "name": "Check headers and IOCs", - "items": {} - } - }, - { - "name": "Search IOCs (ioc-finder)", - "items": {} - }, - { - "name": "Search files (Yara)", - "items": {} - }, - { - "name": "Correlate tickets", - "items": {} - }, - { - "name": "Honeypot access", - "items": { - "name": "...", - "items": {} - } - } - ] - }, - { - "name": "4. Respond", - "color": "#4a148c", - "list": [ - { - "name": "Eradicate malware", - "items": {} - }, - { - "name": "Quarantine host(s)", - "items": {} - }, - { - "name": "Trigger scans", - "items": {} - }, - { - "name": "Update indicators (FW, EDR, SIEM...)", - "items": {} - }, - { - "name": "Autoblock activity when threat intel is received", - "items": {} - }, - { - "name": "Lock/Delete/Reset account", - "items": {} - }, - { - "name": "Lock vault", - "items": {} - }, - { - "name": "Increase authentication", - "items": {} - }, - { - "name": "Get policies from assets", - "items": {} - } - ] - }, - { - "name": "5. Verify", - "color": "#4885ed", - "list": [ - { - "name": "Discover vulnerabilities", - "items": {} - }, - { - "name": "Discover assets", - "items": {} - }, - { - "name": "Ensure policies are followed", - "items": {} - }, - { - "name": "Find Inactive users", - "items": {} - }, - { - "name": "Ensure access rights match HR systems", - "items": {} - }, - { - "name": "Ensure onboarding is followed", - "items": {} - }, - { - "name": "Third party apps in SaaS", - "items": {} - }, - { - "name": "Devices used for your cloud account", - "items": {} - }, - { - "name": "Too much access in GCP/Azure/AWS/ other clouds", - "items": {} - }, - { - "name": "Certificate validation", - "items": {} - }, - { - "name": "Monitor new DNS entries for domain with passive DNS", - "items": {} - }, - { - "name": "Monitor and track password dumps", - "items": {} - }, - { - "name": "Monitor for mentions of domain on darknet sites", - "items": {} - }, - { - "name": "Reporting", - "items": { - "name": "Monthly reports", - "items": { - "name": "...", - "items": {} - } - } - } - ] - } -] \ No newline at end of file diff --git a/functions/usecases/categories.rtf b/functions/usecases/categories.rtf deleted file mode 100644 index e19cf26a..00000000 --- a/functions/usecases/categories.rtf +++ /dev/null @@ -1,555 +0,0 @@ -{\rtf1\ansi\deff0\deflang2057\plain\fs24\fet1 -{\fonttbl -{\f0\froman Arial;} -} -{\info -{\createim\yr2022\mo2\dy20\hr1\min15} -} - -\paperw11907\paperh16840\margl1800\margr1800\margt1440\margb1440 -\slmult0\ltrpar\li0 -{\b\fs28 -Shuffle categories -} -\par\pard\plain -\slmult0\ltrpar\li200 -{\fs24 -1. Collect & Distribute -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -2-way Ticket synchronization -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Email management -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Attachments -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Manage senders -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Manage URLs -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Encode & Decode URLs -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Release a quarantined message -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -EDR to ticket -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Fetch incidents & events -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Quarantine files -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Quarantine host (respond) -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Get host information -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -SIEM to ticket -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -ChatOps -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Threat Intel received -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Domain investigation with LetsEncrypt -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Botnet tracker -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Get running containers -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Assign tickets -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Firewall alerts -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Block/accept policies -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Add addresses and ports to groups -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Support custom URL categories -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Fetch logs for specific address -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -URL filtering -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -IDS/IPS alerts -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Get/Fetch alerts -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Receive alerts real-time -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Get PCAP files -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Get network logs -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Manage policies -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Deduplicate information -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Correlate information -} -\par\pard\plain -\slmult0\ltrpar\li200 -{\fs24 -3. Detect -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Search SIEM (Sigma) -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Network -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Endpoint -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Search EDR (OSQuery) -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Search emails (Phish) -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Check malware -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Check targeted -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Check headers and IOCs -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Search IOCs (ioc-finder) -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Search files (Yara) -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Correlate tickets -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Honeypot access -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -S3 Honeypot -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -SSH Honeypot -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -FTP honeypot -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Network honeypot -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -... -} -\par\pard\plain -\slmult0\ltrpar\li200 -{\fs24 -rich -} -\par\pard\plain -\slmult0\ltrpar\li200 -{\fs24 -5. Verify -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Discover vulnerabilities -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Discover assets -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Ensure policies are followed -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Find Inactive users -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Ensure access rights match HR systems -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Ensure onboarding is followed -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Third party apps in SaaS -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Devices used for your cloud account -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Too much access in GCP/Azure/AWS/ other clouds -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Certificate validation -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Monitor new DNS entries for domain with passive DNS -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Monitor and track password dumps -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Monitor for mentions of domain on darknet sites -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Reporting -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Automation time saved -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Automation money saved -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Incident response report -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Department cost -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Monthly reports -} -\par\pard\plain -\slmult0\ltrpar\li800 -{\fs24 -EDR alerts -} -\par\pard\plain -\slmult0\ltrpar\li800 -{\fs24 -SIEM alerts -} -\par\pard\plain -\slmult0\ltrpar\li800 -{\fs24 -Emails quarantined -} -\par\pard\plain -\slmult0\ltrpar\li800 -{\fs24 -... -} -\par\pard\plain -\slmult0\ltrpar\li200 -{\fs24 -4. Respond -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Eradicate malware -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Quarantine host(s) -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Trigger scans -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Update indicators (FW, EDR, SIEM...) -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Autoblock activity when threat intel is received -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Lock/Delete/Reset account -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Lock vault -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Increase authentication -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Get policies from assets -} -\par\pard\plain -\slmult0\ltrpar\li200 -{\fs24 -2. Enrich -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Internal Enrichment -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Users -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Hostnames -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -IPs -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Departments -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Role -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Software -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -... -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -External historical Enrichment -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -IPs -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -URLs -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Hashes -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Files -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -... -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Realtime -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -File detonation -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -URL detonation -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -PCAP analysis -} -\par\pard\plain -\slmult0\ltrpar\li600 -{\fs24 -Analyze screenshots -} -\par\pard\plain -\slmult0\ltrpar\li400 -{\fs24 -Ticketing webhook verification -} -\par\pard\plain -} \ No newline at end of file diff --git a/functions/usecases/read_categories.py b/functions/usecases/read_categories.py deleted file mode 100644 index 65df43c9..00000000 --- a/functions/usecases/read_categories.py +++ /dev/null @@ -1,66 +0,0 @@ -data = "" -with open("categories.rtf", "r") as tmp: - data = tmp.read() - -fixed_json = [] -linearity = 0 -heading = "" -subheading = "" -subsubheading = "" - -cnt = -1 -subcnt = -1 - -colors = ["#c51152", "#3cba54", "#4885ed", "#4a148c", "#f4c20d"] -for line in data.split("\n"): - if line == "rich": - continue - - if "li" in line: - lisplit = line.split("\\") - try: - linearity = int(lisplit[-1][2]) - except: - pass - - #print("Linearity: %s" % linearity) - - if line.startswith("{") or line.startswith("}"): - continue - - if line.startswith("\\"): - continue - - if linearity == 0: - continue - - if linearity == 2: - #if cnt >= 0: - # for key, value in fixed_json[cnt].items(): - # print(key, value) - - - cnt += 1 - subcnt = -1 - fixed_json.append({"name": line, "color": colors[cnt], "list": []}) - heading = line - elif linearity == 4: - subheading = line - fixed_json[cnt]["list"].append({"name": line, "items": {}}) - subcnt += 1 - elif linearity == 6: - fixed_json[cnt]["list"][subcnt]["items"] = {"name": line, "items": {}} - elif linearity == 8: - fixed_json[cnt]["list"][subcnt]["items"]["items"] = {"name": line, "items": {}} - else: - print("No handler for %s" % line) - -#print(line) -#print(data) -import json -filename = "categories.json" -fixed_json.sort(key=lambda x: x["name"]) -with open(filename, "w+") as tmp: - tmp.write(json.dumps(fixed_json, indent=4)) - -print("Wrote to file %s" % filename) From 074709c7bb6cccd8f570b6505a8d69d20a08db05 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Thu, 26 Jun 2025 20:02:50 +0530 Subject: [PATCH 198/200] fix orborus issues --- backend/go-app/walkoff.go | 24 ++++++++++++++-------- functions/onprem/orborus/orborus.go | 31 +++++++++++++++-------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index c194d091..0aff66c8 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -246,16 +246,16 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { } // This is really the environment's name - NOT org-id - orgId := request.Header.Get("Org-Id") - if len(orgId) == 0 { + environment := request.Header.Get("Org-Id") + if len(environment) == 0 { log.Printf("[AUDIT] No org-id header set") resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Specify the org-id header."}`))) return } - environment := request.Header.Get("org") - if len(environment) == 0 { + orgId := request.Header.Get("org") + if len(orgId) == 0 { //log.Printf("[AUDIT] No 'org' header set (get workflow queue). ") /* resp.WriteHeader(403) @@ -280,9 +280,17 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { //log.Printf("[AUDIT] Get workflow queue for org %s, env %s, orborus label %s", orgId, environment, orborusLabel) ctx := shuffle.GetContext(request) - env, err := shuffle.GetEnvironment(ctx, orgId, "") - if err != nil { - log.Printf("[WARNING] No env found matching %s - continuing without updating orborus anyway: %s", orgId, err) + envs, err := shuffle.GetEnvironments(ctx, orgId) + if err != nil || len(envs) == 0 { + log.Printf("[WARNING] No env found matching %s - continuing without updating orborus anyway: %s", environment, err) + } + + var env *shuffle.Environment + for _, e := range envs { + if e.Name == environment { + env = &e + break + } } timeNow := time.Now().Unix() @@ -450,7 +458,7 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { } } - executionRequests, err := shuffle.GetWorkflowQueue(ctx, orgId, 100) + executionRequests, err := shuffle.GetWorkflowQueue(ctx, environment, 100) if err != nil { // Skipping as this comes up over and over //log.Printf("(2) Failed reading body for workflowqueue: %s", err) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 9b5c87da..e350082b 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -813,21 +813,21 @@ func handleBackendImageDownload(ctx context.Context, images string) error { newImages = append(newImages, curimage) // Force remove the current image to avoid cached layers - if swarmConfig == "run" || swarmConfig == "swarm" { - _, err := dockercli.ImageRemove(ctx, curimage, image.RemoveOptions{ - Force: true, - PruneChildren: true, - }) - - if err != nil { - log.Printf("[ERROR] Failed removing image for re-download: %s", err) - } else { - log.Printf("[DEBUG] Removed image: %s", curimage) - } - } else { - //log.Printf("[DEBUG] Skipping image removal for %s as swarmConfig is not set to run or swarm. Value: %#v", curimage, swarmConfig) - } - +// if swarmConfig == "run" || swarmConfig == "swarm" { +// _, err := dockercli.ImageRemove(ctx, curimage, image.RemoveOptions{ +// Force: true, +// PruneChildren: true, +// }) +// +// if err != nil { +// log.Printf("[ERROR] Failed removing image for re-download: %s", err) +// } else { +// log.Printf("[DEBUG] Removed image: %s", curimage) +// } +// } else { +// //log.Printf("[DEBUG] Skipping image removal for %s as swarmConfig is not set to run or swarm. Value: %#v", curimage, swarmConfig) +// } +// err := shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, curimage) if err != nil { //log.Printf("[ERROR] Failed downloading image: %s", err) @@ -874,6 +874,7 @@ func handleBackendImageDownload(ctx context.Context, images string) error { // Update the service to run with the new image //docker service update --image username/imagename:latest servicename --force serviceUpdateOptions := types.ServiceUpdateOptions{} + service.Spec.TaskTemplate.ForceUpdate++ resp, err := dockercli.ServiceUpdate( ctx, service.ID, From 9bb578616fd4407ddb159d8ffa4b98a0d63c4093 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Sat, 28 Jun 2025 17:26:48 +0530 Subject: [PATCH 199/200] checks for nil pointer --- backend/go-app/walkoff.go | 178 +++++--------------------------------- 1 file changed, 20 insertions(+), 158 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 0aff66c8..08825a52 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -11,7 +11,7 @@ import ( "io" "io/ioutil" "log" - "math/rand" + //"math/rand" "net/http" "net/url" "os" @@ -265,7 +265,8 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { } orborusLabel := request.Header.Get("x-orborus-label") - + _ = orborusLabel + // This section is cloud custom for now auth := request.Header.Get("Authorization") if len(auth) == 0 { @@ -286,13 +287,22 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { } var env *shuffle.Environment - for _, e := range envs { - if e.Name == environment { - env = &e + found := false + for i := range envs { + if envs[i].Name == environment { + env = &envs[i] + found = true break } } + if !found { + log.Printf("[ERROR] Failed to find environment(%s) for org(%s)", environment, orgId) + resp.WriteHeader(400) + resp.Write([]byte(`{"success":false,"reason":"environment not found"}`)) + return + } + timeNow := time.Now().Unix() err = shuffle.HandleOrborusFailover(ctx, request, resp, env) if err != nil { @@ -300,162 +310,14 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { log.Printf("[WARNING] Failed handling Orborus failover: %s", err) } - log.Printf("[DEBUG] Issue with environment ID: %s", orgId) + log.Printf("[DEBUG] Issue with environment ID: %s", environment) return } //log.Printf("Found env: %#v", env) if len(env.OrgId) > 0 { - environment = env.OrgId - } - - // FIXME: Workflow stats disabled for now - // as it caused too many problems - // goal: track docker stuff once a minute and graph it - // For now: Disable this as it caused too many problems - if request.Method == "POST" && true == false { - //log.Printf("[DEBUG] POST to workflowqueue") - if rand.Intn(10) == 0 { - // Parse out body - body, err := ioutil.ReadAll(request.Body) - if err == nil { - - // Parse out CPU, memory and disk. - - var envData shuffle.OrborusStats - err = json.Unmarshal(body, &envData) - if err == nil && !envData.Swarm && !envData.Kubernetes && (envData.CPU > 0 || envData.Memory > 0 || envData.Disk > 0) { - - // Set the input in memory - envData.OrgId = orgId - envData.Environment = environment - envData.OrborusLabel = orborusLabel - envData.Timestamp = time.Now().Unix() - - if envData.CPU > 0 && envData.MaxCPU > 0 { - envData.CPUPercent = float64(envData.CPU) / float64(envData.MaxCPU) - } - - if envData.Memory > 0 && envData.MaxMemory > 0 { - envData.MemoryPercent = float64(envData.Memory) / float64(envData.MaxMemory) - } - - // Check if CPU percent constantly has stayed above X% for the last Y requests - percentageCheck := 90 - concurrentChecks := 2 - - //if int(envData.CPUPercent) > percentageCheck { - // Get cached data - percentages := []float64{} - cacheKey := fmt.Sprintf("%s_%s_percent", orgId, strings.ToLower(environment)) - - // Marshal float list into []byte - cacheData := []byte{} - cache, err := shuffle.GetCache(ctx, cacheKey) - if err == nil { - // Unmarshal into percentages - cacheData := []byte(cache.([]uint8)) - err = json.Unmarshal(cacheData, &percentages) - if err != nil { - log.Printf("[INFO] error in cache unmarshal for percentages: %s", err) - } - - if len(percentages) > concurrentChecks { - percentages = percentages[:concurrentChecks] - } - - percentages = append(percentages, envData.CPUPercent) - if len(percentages) > concurrentChecks { - //log.Printf("[INFO] Checking percentages: %v", percentages) - - // percentageCheck := 1 - sendAlert := true - for _, p := range percentages { - if int(p) < percentageCheck { - //log.Printf("[AUDIT] CPU percent is below %d: %d", percentageCheck, int(p)) - sendAlert = false - break - } - } - - if sendAlert { - log.Printf("[INFO] CPU percent has been above %d percent for the last 5 requests. Sending alert. Env: %s, org: %s", percentageCheck, environment, orgId) - - // Set notification + alert for organization - err = shuffle.CreateOrgNotification( - ctx, - fmt.Sprintf("CPU percent has been above %d percent", percentageCheck), - fmt.Sprintf("A environment %s has been using more than %d\\% CPU for the last 5 requests.", environment, percentageCheck), - fmt.Sprintf("/admin?tab=environments"), - environment, - true, - ) - - if err != nil { - log.Printf("[ERROR] error creating notification: %s", err) - } - - org, err := shuffle.GetOrg(ctx, environment) - if err == nil { - foundRecommendation := false - for _, recommendation := range org.Priorities { - if strings.Contains(recommendation.Name, "CPU") { - foundRecommendation = true - break - } - } - - if !foundRecommendation { - // Add to start of org.Priorities - org, _ = shuffle.AddPriority(*org, shuffle.Priority{ - Name: fmt.Sprintf("High CPU in environment %s", orgId), - Description: fmt.Sprintf("The environment %s has been using more than %d percent CPU. This indicates you may need to look at scaling.", orgId, percentageCheck), - Type: "scale", - Active: true, - URL: fmt.Sprintf("/admin?tab=environments"), - Severity: 1, - }, false) - - //Make last item the first item - org.Priorities = append([]shuffle.Priority{org.Priorities[len(org.Priorities)-1]}, org.Priorities[:len(org.Priorities)-1]...) - err = shuffle.SetOrg(ctx, *org, org.Id) - if err != nil { - log.Printf("[ERROR] Problem setting org: %s", err) - } - } - } - } - - if len(percentages) > 1 { - percentages = percentages[1:] - } - } - - // Marshal float list into []byte - } else { - //log.Printf("[ERROR] Failed getting cache: %s", err) - percentages = append(percentages, envData.CPUPercent) - } - - if len(percentages) > 0 { - //log.Printf("[DEBUG] Setting cache for %s: %#v", cacheKey, percentages) - cacheData, err = json.Marshal(percentages) - if err != nil { - log.Printf("[INFO] error in cache marshal: %s", err) - } - - // Add the new data - go shuffle.SetCache(ctx, cacheKey, cacheData, 5) - } - } - - //log.Printf("CPU percent: %f", envData.CPUPercent) - //log.Printf("Memory percent: %f", envData.MemoryPercent*100) - - go shuffle.SetenvStats(ctx, envData) - } - } + orgId = env.OrgId } executionRequests, err := shuffle.GetWorkflowQueue(ctx, environment, 100) @@ -486,10 +348,10 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { } } - if len(orgId) > 0 { - env, err := shuffle.GetEnvironment(ctx, orgId, foundId) + if len(environment) > 0 { + env, err := shuffle.GetEnvironment(ctx, environment, foundId) if err != nil { - log.Printf("[WARNING] No env found matching %s - continuing without updating orborus anyway: %s", orgId, err) + log.Printf("[WARNING] No env found matching %s - continuing without updating orborus anyway: %s", environment, err) //resp.WriteHeader(401) //resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No env found matching %s"}`, id))) //return From 0be2b9fe93cb9d4e8277dc7771bc3c55a9fe7092 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Sat, 28 Jun 2025 19:18:52 +0530 Subject: [PATCH 200/200] log org from the header --- backend/go-app/walkoff.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 08825a52..0f078dca 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -254,9 +254,9 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { return } - orgId := request.Header.Get("org") + orgId := request.Header.Get("Org") if len(orgId) == 0 { - //log.Printf("[AUDIT] No 'org' header set (get workflow queue). ") + log.Printf("[AUDIT] No 'org' header set (get workflow queue). ") /* resp.WriteHeader(403) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Specify the org header. This can be done by setting the 'ORG' environment variable for Orborus to your Org ID in Shuffle"}`)))