Merge remote-tracking branch 'origin/1.3.0' into dhaval055/1.3.0
This commit is contained in:
@@ -71,8 +71,8 @@ SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500 # 1500 by default
|
||||
# the container to the docker0
|
||||
SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE=eth0
|
||||
|
||||
# Used for auto-cleanup of containers. REALLY important at scale.
|
||||
SHUFFLE_CONTAINER_AUTO_CLEANUP=false
|
||||
# Used for auto-cleanup of containers. REALLY important at scale. Set to false to see all container info.
|
||||
SHUFFLE_CONTAINER_AUTO_CLEANUP=true
|
||||
SHUFFLE_ELASTIC=true
|
||||
SHUFFLE_LOGS_DISABLED=false
|
||||
SHUFFLE_CHAT_DISABLED=false # Controls support chat
|
||||
|
||||
@@ -3519,7 +3519,7 @@ class AppBase:
|
||||
if self.action["app_name"].lower() == "shuffle tools":
|
||||
timeout = 55
|
||||
|
||||
timeout = 30
|
||||
#timeout = 30
|
||||
|
||||
try:
|
||||
executor = concurrent.futures.ThreadPoolExecutor()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module shuffle-shared
|
||||
|
||||
//replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared
|
||||
replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared
|
||||
|
||||
go 1.19
|
||||
|
||||
|
||||
+71
-109
@@ -84,62 +84,8 @@ var registryName = "registry.hub.docker.com"
|
||||
var runningEnvironment = "onprem"
|
||||
|
||||
var syncUrl = "https://shuffler.io"
|
||||
var syncSubUrl = "https://shuffler.io"
|
||||
|
||||
var dbclient *datastore.Client
|
||||
|
||||
type Userapi struct {
|
||||
Username string `datastore:"username"`
|
||||
ApiKey string `datastore:"apikey"`
|
||||
}
|
||||
|
||||
type ExecutionInfo struct {
|
||||
TotalApiUsage int64 `json:"total_api_usage" datastore:"total_api_usage"`
|
||||
TotalWorkflowExecutions int64 `json:"total_workflow_executions" datastore:"total_workflow_executions"`
|
||||
TotalAppExecutions int64 `json:"total_app_executions" datastore:"total_app_executions"`
|
||||
TotalCloudExecutions int64 `json:"total_cloud_executions" datastore:"total_cloud_executions"`
|
||||
TotalOnpremExecutions int64 `json:"total_onprem_executions" datastore:"total_onprem_executions"`
|
||||
DailyApiUsage int64 `json:"daily_api_usage" datastore:"daily_api_usage"`
|
||||
DailyWorkflowExecutions int64 `json:"daily_workflow_executions" datastore:"daily_workflow_executions"`
|
||||
DailyAppExecutions int64 `json:"daily_app_executions" datastore:"daily_app_executions"`
|
||||
DailyCloudExecutions int64 `json:"daily_cloud_executions" datastore:"daily_cloud_executions"`
|
||||
DailyOnpremExecutions int64 `json:"daily_onprem_executions" datastore:"daily_onprem_executions"`
|
||||
}
|
||||
|
||||
// "Execution by status"
|
||||
// Execution history
|
||||
//type GlobalStatistics struct {
|
||||
// BackendExecutions int64 `json:"backend_executions" datastore:"backend_executions"`
|
||||
// WorkflowCount int64 `json:"workflow_count" datastore:"workflow_count"`
|
||||
// ExecutionCount int64 `json:"execution_count" datastore:"execution_count"`
|
||||
// ExecutionSuccessCount int64 `json:"execution_success_count" datastore:"execution_success_count"`
|
||||
// ExecutionAbortCount int64 `json:"execution_abort_count" datastore:"execution_abort_count"`
|
||||
// ExecutionFailureCount int64 `json:"execution_failure_count" datastore:"execution_failure_count"`
|
||||
// ExecutionPendingCount int64 `json:"execution_pending_count" datastore:"execution_pending_count"`
|
||||
// AppUsageCount int64 `json:"app_usage_count" datastore:"app_usage_count"`
|
||||
// TotalAppsCount int64 `json:"total_apps_count" datastore:"total_apps_count"`
|
||||
// SelfMadeAppCount int64 `json:"self_made_app_count" datastore:"self_made_app_count"`
|
||||
// WebhookUsageCount int64 `json:"webhook_usage_count" datastore:"webhook_usage_count"`
|
||||
// Baseline map[string]int64 `json:"baseline" datastore:"baseline"`
|
||||
//}
|
||||
|
||||
type ParsedOpenApi struct {
|
||||
Body string `datastore:"body,noindex" json:"body"`
|
||||
ID string `datastore:"id" json:"id"`
|
||||
Success bool `datastore:"success,omitempty" json:"success,omitempty"`
|
||||
}
|
||||
|
||||
// Limits set for a user so that they can't do a shitload
|
||||
type UserLimits struct {
|
||||
DailyApiUsage int64 `json:"daily_api_usage" datastore:"daily_api_usage"`
|
||||
DailyWorkflowExecutions int64 `json:"daily_workflow_executions" datastore:"daily_workflow_executions"`
|
||||
DailyCloudExecutions int64 `json:"daily_cloud_executions" datastore:"daily_cloud_executions"`
|
||||
DailyTriggers int64 `json:"daily_triggers" datastore:"daily_triggers"`
|
||||
DailyMailUsage int64 `json:"daily_mail_usage" datastore:"daily_mail_usage"`
|
||||
MaxTriggers int64 `json:"max_triggers" datastore:"max_triggers"`
|
||||
MaxWorkflows int64 `json:"max_workflows" datastore:"max_workflows"`
|
||||
}
|
||||
|
||||
type retStruct struct {
|
||||
Success bool `json:"success"`
|
||||
SyncFeatures shuffle.SyncFeatures `json:"sync_features"`
|
||||
@@ -148,43 +94,6 @@ type retStruct struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// Saves some data, not sure what to have here lol
|
||||
type UserAuth struct {
|
||||
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Workflows []string `json:"workflows" datastore:"workflows"`
|
||||
Username string `json:"username" datastore:"username"`
|
||||
Fields []UserAuthField `json:"fields" datastore:"fields"`
|
||||
}
|
||||
|
||||
type UserAuthField struct {
|
||||
Key string `json:"key" datastore:"key"`
|
||||
Value string `json:"value" datastore:"value,noindex"`
|
||||
}
|
||||
|
||||
// Not environment, but execution environment
|
||||
//type Environment struct {
|
||||
// Name string `datastore:"name"`
|
||||
// Type string `datastore:"type"`
|
||||
// Registered bool `datastore:"registered"`
|
||||
// Default bool `datastore:"default" json:"default"`
|
||||
// Archived bool `datastore:"archived" json:"archived"`
|
||||
// Id string `datastore:"id" json:"id"`
|
||||
// OrgId string `datastore:"org_id" json:"org_id"`
|
||||
//}
|
||||
|
||||
// timeout maybe? idk
|
||||
type session struct {
|
||||
Username string `datastore:"Username,noindex"`
|
||||
Id string `datastore:"Id,noindex"`
|
||||
Session string `datastore:"session,noindex"`
|
||||
}
|
||||
|
||||
type loginStruct struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
Firstname string `json:"firstname"`
|
||||
Lastname string `json:"lastname"`
|
||||
@@ -1102,7 +1011,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
|
||||
userOrgs = shuffle.SortOrgList(userOrgs)
|
||||
orgPriorities := org.Priorities
|
||||
if len(org.Priorities) < 10 {
|
||||
log.Printf("[WARNING] Should find and add priorities as length is less than 10 for org %s", userInfo.ActiveOrg.Id)
|
||||
//log.Printf("[WARNING] Should find and add priorities as length is less than 10 for org %s", userInfo.ActiveOrg.Id)
|
||||
newPriorities, err := shuffle.GetPriorities(ctx, userInfo, org)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed getting new priorities for org %s: %s", org.Id, err)
|
||||
@@ -3750,16 +3659,69 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
||||
|
||||
// Check if it's 1 in 10 (10% chance random)
|
||||
backupJob := shuffle.BackupJob{}
|
||||
|
||||
// Check if workflow backup is active
|
||||
// Check if app backup is active
|
||||
ctx := context.Background()
|
||||
|
||||
foundUser := org.Users[0]
|
||||
for _, user := range org.Users {
|
||||
if user.Role == "admin" {
|
||||
foundUser = user
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if org.SyncConfig.WorkflowBackup {
|
||||
workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err)
|
||||
} else {
|
||||
backupJob.Workflows = workflows
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed marshalling backup job: %s", err)
|
||||
backupJobData = []byte{}
|
||||
}
|
||||
|
||||
|
||||
client := &http.Client{}
|
||||
syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl)
|
||||
req, err := http.NewRequest(
|
||||
"GET",
|
||||
"POST",
|
||||
syncUrl,
|
||||
nil,
|
||||
bytes.NewBuffer(backupJobData),
|
||||
)
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey))
|
||||
|
||||
//log.Printf("[INFO] Sending org sync with autho %s", org.SyncConfig.Apikey)
|
||||
|
||||
newresp, err := client.Do(req)
|
||||
if err != nil {
|
||||
//log.Printf("Failed request in org sync: %s", err)
|
||||
@@ -3775,7 +3737,7 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
||||
//log.Printf("Remote Data: %s", respBody)
|
||||
err = remoteOrgJobController(org, respBody)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed job controller run for %s: %s", respBody, err)
|
||||
//log.Printf("[ERROR] Failed cloud sync job controller run for '%s': %s", respBody, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -3819,7 +3781,7 @@ func runInitEs(ctx context.Context) {
|
||||
activeOrgs, err := shuffle.GetAllOrgs(ctx)
|
||||
|
||||
setUsers := false
|
||||
//log.Printf("ORGS: %d", len(activeOrgs))
|
||||
_ = setUsers
|
||||
if err != nil {
|
||||
if fmt.Sprintf("%s", err) == "EOF" {
|
||||
time.Sleep(7 * time.Second)
|
||||
@@ -3874,7 +3836,7 @@ func runInitEs(ctx context.Context) {
|
||||
|
||||
if len(activeOrgs) == 1 {
|
||||
if len(activeOrgs[0].Users) == 0 {
|
||||
log.Printf("ORG doesn't have any users??")
|
||||
log.Printf("[ERROR] Main Org doesn't have any user. Creating.")
|
||||
|
||||
users, err := shuffle.GetAllUsers(ctx)
|
||||
if err != nil && len(users) == 0 {
|
||||
@@ -3907,10 +3869,9 @@ func runInitEs(ctx context.Context) {
|
||||
|
||||
if strings.Contains(os.Getenv("SHUFFLE_OPENSEARCH_URL"), "https") {
|
||||
log.Printf("[INFO] Waiting during init to make sure the opensearch instance is up and running with security features properly")
|
||||
time.Sleep(30 * time.Second)
|
||||
time.Sleep(15 * time.Second)
|
||||
}
|
||||
|
||||
_ = setUsers
|
||||
schedules, err := shuffle.GetAllSchedules(ctx, "ALL")
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed getting schedules during service init: %s", err)
|
||||
@@ -4054,11 +4015,11 @@ func runInitEs(ctx context.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Should start schedule for org %s (%s)", org.Name, org.Id)
|
||||
log.Printf("[DEBUG] Should start cloud schedule for org %s (%s)", org.Name, org.Id)
|
||||
job := func() {
|
||||
err := remoteOrgJobHandler(org, interval)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed request with remote org setup (2): %s", err)
|
||||
log.Printf("[ERROR] Failed request with remote org setup for org %s (2): %s", org.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4760,7 +4721,7 @@ func runInit(ctx context.Context) {
|
||||
job := func() {
|
||||
err := remoteOrgJobHandler(org, interval)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed request with remote org setup (2): %s", err)
|
||||
log.Printf("[ERROR] Failed request with remote org setup (3): %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5244,7 +5205,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
b, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
log.Printf("Failed marshaling api key data: %s", err)
|
||||
log.Printf("[ERROR] Failed marshaling api key data: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed cloud sync: %s"}`, err)))
|
||||
return
|
||||
@@ -5271,7 +5232,8 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
//log.Printf("Respbody: %s", string(respBody))
|
||||
log.Printf("[DEBUG] Respbody from sync: %s", string(respBody))
|
||||
|
||||
responseData := retStruct{}
|
||||
err = json.Unmarshal(respBody, &responseData)
|
||||
if err != nil {
|
||||
@@ -5896,6 +5858,8 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/_ah/health", shuffle.HealthCheckHandler)
|
||||
r.HandleFunc("/api/v1/health", shuffle.RunOpsHealthCheck).Methods("GET", "OPTIONS")
|
||||
|
||||
r.HandleFunc("/api/v1/health/stats", shuffle.GetOpsDashboardStats).Methods("GET", "OPTIONS")
|
||||
|
||||
// Make user related locations
|
||||
// Fix user changes with org
|
||||
r.HandleFunc("/api/v1/users/login", shuffle.HandleLogin).Methods("POST", "OPTIONS")
|
||||
@@ -5967,10 +5931,6 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", shuffle.SetAuthenticationConfig).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/apps/authentication/{appauthId}", shuffle.DeleteAppAuthentication).Methods("DELETE", "OPTIONS")
|
||||
|
||||
// Related to NFT things
|
||||
r.HandleFunc("/api/v1/workflows/collections/load", shuffle.LoadCollections).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/collections/{key}", shuffle.HandleGetCollection).Methods("GET", "OPTIONS")
|
||||
|
||||
// Related to use-cases that are not directly workflows.
|
||||
r.HandleFunc("/api/v1/workflows/usecases/{key}", shuffle.HandleGetUsecase).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/usecases", shuffle.LoadUsecases).Methods("GET", "OPTIONS")
|
||||
@@ -6000,6 +5960,9 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/workflows/{key}", shuffle.GetSpecificWorkflow).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/workflows/recommend", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS")
|
||||
|
||||
// First v2 API
|
||||
r.HandleFunc("/api/v2/workflows/{key}/executions", shuffle.GetWorkflowExecutionsV2).Methods("GET", "OPTIONS")
|
||||
|
||||
// New for recommendations in Shuffle
|
||||
r.HandleFunc("/api/v1/recommendations/get_actions", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/recommendations/modify", shuffle.HandleRecommendationAction).Methods("POST", "OPTIONS")
|
||||
@@ -6114,7 +6077,6 @@ func initHandlers() {
|
||||
func main() {
|
||||
|
||||
initHandlers()
|
||||
go shuffle.InitOpsWorkflow()
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
hostname = "MISSING"
|
||||
|
||||
@@ -700,64 +700,6 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Removed as UserInput is now handled as an app
|
||||
if actionResult.Status == "WAITING" && actionResult.Action.AppName == "User Input" {
|
||||
log.Printf("[INFO] SHOULD WAIT A BIT AND RUN USER INPUT! WAITING!")
|
||||
|
||||
var trigger shuffle.Trigger
|
||||
err = json.Unmarshal([]byte(actionResult.Result), &trigger)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed unmarshaling actionresult for user input: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
orgId := workflowExecution.ExecutionOrg
|
||||
if len(workflowExecution.OrgId) == 0 && len(workflowExecution.Workflow.OrgId) > 0 {
|
||||
orgId = workflowExecution.Workflow.OrgId
|
||||
}
|
||||
|
||||
err := handleUserInput(trigger, orgId, workflowExecution.Workflow.ID, workflowExecution.ExecutionId)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed userinput handler: %s", err)
|
||||
|
||||
actionResult.Result = fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)
|
||||
|
||||
workflowExecution.Results = append(workflowExecution.Results, actionResult)
|
||||
workflowExecution.Status = "ABORTED"
|
||||
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed to set execution during wait: %s", err)
|
||||
} else {
|
||||
log.Printf("[INFO] Successfully set the execution %s to waiting.", workflowExecution.ExecutionId)
|
||||
}
|
||||
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Error: %s"}`, err)))
|
||||
return
|
||||
} else {
|
||||
log.Printf("[INFO] Successful userinput handler")
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "CLOUD IS DONE"}`)))
|
||||
|
||||
actionResult.Result = `{"success": True, "reason": "Waiting for user feedback based on configuration"}`
|
||||
|
||||
workflowExecution.Results = append(workflowExecution.Results, actionResult)
|
||||
workflowExecution.Status = actionResult.Status
|
||||
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed setting userinput: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Successfully set the execution to waiting.")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
|
||||
}
|
||||
|
||||
@@ -1750,6 +1692,8 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
return shuffle.WorkflowExecution{}, "Cloud not implemented yet", errors.New("Cloud not implemented yet")
|
||||
}
|
||||
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_cloud")
|
||||
|
||||
// What it needs to know:
|
||||
// 1. Parameters
|
||||
if len(workflowExecution.Workflow.Actions) == 1 {
|
||||
@@ -1763,13 +1707,11 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
// If worker, should this backend be a proxy? I think so.
|
||||
return shuffle.WorkflowExecution{}, "Cloud not implemented yet (2)", errors.New("Cloud not implemented yet")
|
||||
}
|
||||
} else {
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_onprem")
|
||||
}
|
||||
|
||||
//err = increaseStatisticsField(ctx, "workflow_executions", workflow.ID, 1, workflowExecution.ExecutionOrg)
|
||||
//if err != nil {
|
||||
// log.Printf("Failed to increase stats execution stats: %s", err)
|
||||
//}
|
||||
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions")
|
||||
return workflowExecution, "", nil
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
frontend:
|
||||
image: ghcr.io/shuffle/shuffle-frontend:latest
|
||||
image: ghcr.io/shuffle/shuffle-frontend:nightly
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
@@ -61,8 +61,9 @@ services:
|
||||
hostname: shuffle-opensearch
|
||||
container_name: shuffle-opensearch
|
||||
environment:
|
||||
- bootstrap.memory_lock=true
|
||||
- "OPENSEARCH_JAVA_OPTS=-Xms2048m -Xmx2048m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
|
||||
- bootstrap.memory_lock=true
|
||||
- DISABLE_PERFORMANCE_ANALYZER_AGENT_CLI=true
|
||||
- cluster.initial_master_nodes=shuffle-opensearch
|
||||
- cluster.routing.allocation.disk.threshold_enabled=false
|
||||
- cluster.name=shuffle-cluster
|
||||
|
||||
@@ -326,6 +326,7 @@ const App = (message, props) => {
|
||||
path="/usecases"
|
||||
element={
|
||||
<Dashboard
|
||||
userdata={userdata}
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
|
||||
@@ -38,7 +38,7 @@ import { toast } from 'react-toastify';
|
||||
cytoscape.use(edgehandles);
|
||||
|
||||
const svgSize = "40px"
|
||||
const parsedDatatypeImages = {
|
||||
export const parsedDatatypeImages = {
|
||||
"SIEM": encodeURI(`data:image/svg+xml;utf-8,<svg fill="rgb(248,90,62)" width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M6.93767 0C8.71083 0 10.4114 0.704386 11.6652 1.9582C12.919 3.21202 13.6234 4.91255 13.6234 6.68571C13.6234 8.34171 13.0165 9.864 12.0188 11.0366L12.2965 11.3143H13.1091L18.252 16.4571L16.7091 18L11.5662 12.8571V12.0446L11.2885 11.7669C10.116 12.7646 8.59367 13.3714 6.93767 13.3714C5.16451 13.3714 3.46397 12.667 2.21015 11.4132C0.956339 10.1594 0.251953 8.45888 0.251953 6.68571C0.251953 4.91255 0.956339 3.21202 2.21015 1.9582C3.46397 0.704386 5.16451 0 6.93767 0ZM6.93767 2.05714C4.36624 2.05714 2.3091 4.11429 2.3091 6.68571C2.3091 9.25714 4.36624 11.3143 6.93767 11.3143C9.5091 11.3143 11.5662 9.25714 11.5662 6.68571C11.5662 4.11429 9.5091 2.05714 6.93767 2.05714Z" /></svg>`),
|
||||
|
||||
"CASES": encodeURI(`data:image/svg+xml;utf-8,<svg fill="rgb(248,90,62)" width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M15.6408 8.39233H18.0922V10.0287H15.6408V8.39233ZM0.115234 8.39233H2.56663V10.0287H0.115234V8.39233ZM9.92083 0.21051V2.66506H8.28656V0.21051H9.92083ZM3.31839 2.25596L5.05889 4.00687L3.89856 5.16051L2.15807 3.42596L3.31839 2.25596ZM13.1485 3.99869L14.8808 2.25596L16.0493 3.42596L14.3088 5.16051L13.1485 3.99869ZM9.10369 4.30142C10.404 4.30142 11.651 4.81863 12.5705 5.73926C13.4899 6.65989 14.0065 7.90854 14.0065 9.21051C14.0065 11.0269 13.0178 12.6141 11.5551 13.4651V14.9378C11.5551 15.1548 11.469 15.3629 11.3158 15.5163C11.1625 15.6698 10.9547 15.756 10.738 15.756H7.46943C7.25271 15.756 7.04487 15.6698 6.89163 15.5163C6.73839 15.3629 6.6523 15.1548 6.6523 14.9378V13.4651C5.18963 12.6141 4.2009 11.0269 4.2009 9.21051C4.2009 7.90854 4.71744 6.65989 5.63689 5.73926C6.55635 4.81863 7.80339 4.30142 9.10369 4.30142ZM10.738 16.5741V17.3923C10.738 17.6093 10.6519 17.8174 10.4986 17.9709C10.3454 18.1243 10.1375 18.2105 9.92083 18.2105H8.28656C8.06984 18.2105 7.862 18.1243 7.70876 17.9709C7.55552 17.8174 7.46943 17.6093 7.46943 17.3923V16.5741H10.738ZM8.28656 14.1196H9.92083V12.3769C11.3345 12.0169 12.3722 10.7323 12.3722 9.21051C12.3722 8.34253 12.0279 7.5101 11.4149 6.89634C10.8019 6.28259 9.97056 5.93778 9.10369 5.93778C8.23683 5.93778 7.40546 6.28259 6.79249 6.89634C6.17953 7.5101 5.83516 8.34253 5.83516 9.21051C5.83516 10.7323 6.87292 12.0169 8.28656 12.3769V14.1196Z" /></svg>`),
|
||||
@@ -713,7 +713,7 @@ const AppFramework = (props) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
||||
//console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
||||
if (discoveryWrapper === undefined || discoveryWrapper.id === "SHUFFLE" || discoveryWrapper.id === undefined || cy === undefined) {
|
||||
setDiscoveryData({})
|
||||
|
||||
@@ -874,7 +874,7 @@ const AppFramework = (props) => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
console.log("New selected app: ", newSelectedApp, discoveryData)
|
||||
//console.log("New selected app: ", newSelectedApp, discoveryData)
|
||||
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||
return
|
||||
}
|
||||
@@ -1856,8 +1856,6 @@ const AppFramework = (props) => {
|
||||
//autounselectify={true}
|
||||
var usecasediff = -100
|
||||
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
|
||||
console.log("Background: ", bgColor)
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
|
||||
<div style={{position: "absolute"}}>
|
||||
@@ -2067,8 +2065,8 @@ const AppFramework = (props) => {
|
||||
|
||||
const foundelement = cy.getElementById(discoveryData.id)
|
||||
if (foundelement !== undefined && foundelement !== null) {
|
||||
console.log("element: ", foundelement)
|
||||
console.log("DISC: ", discoveryData)
|
||||
//console.log("element: ", foundelement)
|
||||
//console.log("DISC: ", discoveryData)
|
||||
foundelement.data("large_image", parsedDatatypeImages[discoveryData.id.toUpperCase()])
|
||||
foundelement.data("text_margin_y", "14px")
|
||||
foundelement.data("margin_x", "32px")
|
||||
|
||||
@@ -200,7 +200,7 @@ const Appsearch = props => {
|
||||
const CustomHits = connectHits(InputHits)
|
||||
|
||||
return (
|
||||
<div style={{width: "100%", textAlign: "center", position: "relative", height: "100%",}}>
|
||||
<div style={{width: 287, height: 295, padding: "16px 16px 267px 16px", alignItems: "center", gap: 138,}}>
|
||||
<InstantSearch searchClient={searchClient} indexName="appsearch">
|
||||
{/* showSearch === false ? null :
|
||||
<div style={{maxWidth: 450, margin: "auto", }}>
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
Grid,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField,
|
||||
Zoom,
|
||||
} from "@mui/material";
|
||||
|
||||
|
||||
@@ -294,9 +294,6 @@ const AuthenticationData = (props) => {
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
height: 50,
|
||||
fontSize: "1em",
|
||||
},
|
||||
disableUnderline: true,
|
||||
@@ -386,7 +383,6 @@ const AuthenticationData = (props) => {
|
||||
PaperProps={{
|
||||
style: {
|
||||
pointerEvents: "auto",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 600,
|
||||
minHeight: 600,
|
||||
@@ -441,9 +437,6 @@ const AuthenticationData = (props) => {
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
height: 50,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
|
||||
+1010
-316
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,280 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
TextField,
|
||||
IconButton,
|
||||
Button,
|
||||
Typography,
|
||||
Grid,
|
||||
Paper,
|
||||
Chip,
|
||||
Checkbox,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
BarChart,
|
||||
RadialBarChart,
|
||||
RadialAreaChart,
|
||||
RadialAxis,
|
||||
StackedBarSeries,
|
||||
TooltipArea,
|
||||
ChartTooltip,
|
||||
TooltipTemplate,
|
||||
RadialAreaSeries,
|
||||
RadialPointSeries,
|
||||
RadialArea,
|
||||
RadialLine,
|
||||
TreeMap,
|
||||
TreeMapSeries,
|
||||
TreeMapLabel,
|
||||
TreeMapRect,
|
||||
Line,
|
||||
LineChart,
|
||||
LineSeries,
|
||||
LinearYAxis,
|
||||
LinearXAxis,
|
||||
LinearYAxisTickSeries,
|
||||
LinearXAxisTickSeries,
|
||||
Area,
|
||||
AreaChart,
|
||||
AreaSeries,
|
||||
AreaSparklineChart,
|
||||
PointSeries,
|
||||
GridlineSeries,
|
||||
Gridline,
|
||||
Stripes,
|
||||
Gradient,
|
||||
GradientStop,
|
||||
LinearXAxisTickLabel,
|
||||
} from 'reaviz';
|
||||
|
||||
const LineChartWrapper = ({keys, inputname, height, width}) => {
|
||||
const [hovered, setHovered] = useState("");
|
||||
const inputdata = keys.data === undefined ? keys : keys.data
|
||||
|
||||
return (
|
||||
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, }}>
|
||||
<Typography variant="h6" style={{marginBotton: 15, }}>
|
||||
{inputname}
|
||||
</Typography>
|
||||
<BarChart
|
||||
width={"100%"}
|
||||
height={height}
|
||||
data={inputdata}
|
||||
gridlines={
|
||||
<GridlineSeries line={<Gridline direction="all" />} />
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const AppStats = (defaultprops) => {
|
||||
const { globalUrl, selectedOrganization, userdata, } = defaultprops;
|
||||
const [keys, setKeys] = useState([])
|
||||
const [searches, setSearches] = useState([]);
|
||||
const [clickData, setClickData] = useState(undefined);
|
||||
const [conversionData, setConversionData] = useState(undefined);
|
||||
const [statistics, setStatistics] = useState(undefined);
|
||||
const [appRuns, setAppruns] = useState(undefined);
|
||||
const [workflowRuns, setWorkflowRuns] = useState(undefined);
|
||||
const [subflowRuns, setSubflowRuns] = useState(undefined);
|
||||
|
||||
const handleDataSetting = (inputdata, grouping) => {
|
||||
if (inputdata === undefined || inputdata === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const dailyStats = inputdata.daily_statistics
|
||||
if (dailyStats === undefined || dailyStats === null) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Looking at daily data: ", inputdata)
|
||||
|
||||
var appRuns = {
|
||||
"key": "App Runs",
|
||||
"data": []
|
||||
}
|
||||
|
||||
var workflowRuns = {
|
||||
"key": "Workflow Runs (includes subflows)",
|
||||
"data": []
|
||||
}
|
||||
|
||||
var subflowRuns = {
|
||||
"key": "Subflow Runs",
|
||||
"data": []
|
||||
}
|
||||
|
||||
for (let key in dailyStats) {
|
||||
// Always skips first one as it has accumulated data in it
|
||||
if (key === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const item = dailyStats[key]
|
||||
|
||||
if (item["date"] === undefined) {
|
||||
console.log("No date: ", item)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if app_executions key in item
|
||||
if (item["app_executions"] !== undefined && item["app_executions"] !== null) {
|
||||
appRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["app_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
// Check if workflow_executions key in item
|
||||
if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) {
|
||||
workflowRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["workflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (item["subflow_executions"] !== undefined && item["subflow_executions"] !== null) {
|
||||
subflowRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["subflow_executions"]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Adds data for today
|
||||
console.log("Inputdata: ", inputdata)
|
||||
if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
|
||||
appRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_app_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
|
||||
workflowRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_workflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (inputdata["daily_subflow_executions"] !== undefined && inputdata["daily_subflow_executions"] !== null) {
|
||||
subflowRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_subflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
setSubflowRuns(subflowRuns)
|
||||
setWorkflowRuns(workflowRuns)
|
||||
setAppruns(appRuns)
|
||||
}
|
||||
|
||||
const getStats = () => {
|
||||
fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflows :O!: ", response.status);
|
||||
return;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
return
|
||||
}
|
||||
|
||||
setStatistics(responseJson)
|
||||
handleDataSetting(responseJson, "day")
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error: ", error)
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getStats()
|
||||
}, [])
|
||||
|
||||
const paperStyle = {
|
||||
textAlign: "center",
|
||||
padding: 40,
|
||||
margin: 5,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
maxWidth: 300,
|
||||
}
|
||||
|
||||
const data = (
|
||||
<div className="content" style={{width: "100%", margin: "auto", }}>
|
||||
<Typography variant="body1" style={{margin: "auto", marginLeft: 10, marginBottom: 20, }}>
|
||||
All Stat widgets are monthly and gathered from <a
|
||||
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
|
||||
target="_blank"
|
||||
style={{ textDecoration: "none", color: "#f85a3e",}}
|
||||
>Your Organization Statistics. </a>
|
||||
This is a feature to help give you more insight into Shuffle, and will be populating over time.
|
||||
</Typography>
|
||||
{statistics !== undefined ?
|
||||
<div style={{display: "flex", textAlign: "center",}}>
|
||||
<Paper style={paperStyle}>
|
||||
<Typography variant="h4">
|
||||
{statistics.monthly_workflow_executions}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
Workflow Runs
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper style={paperStyle}>
|
||||
<Typography variant="h4">
|
||||
{statistics.monthly_app_executions}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
App Runs
|
||||
</Typography>
|
||||
</Paper>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
{appRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={appRuns} height={300} width={"100%"} inputname={"Daily App Runs"}/>
|
||||
}
|
||||
|
||||
{workflowRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={workflowRuns} height={300} width={"100%"} inputname={"Daily Workflow Runs (including subflows)"}/>
|
||||
}
|
||||
|
||||
{subflowRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={subflowRuns} height={300} width={"100%"} inputname={"Subflow Runs"}/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
const dataWrapper = (
|
||||
<div style={{ maxWidth: 1366, margin: "auto" }}>{data}</div>
|
||||
);
|
||||
|
||||
return dataWrapper;
|
||||
}
|
||||
|
||||
export default AppStats;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ import { MuiChipsInput } from "mui-chips-input";
|
||||
import UsecaseSearch from "../components/UsecaseSearch.jsx"
|
||||
import WorkflowGrid from "../components/WorkflowGrid.jsx"
|
||||
import dayjs from 'dayjs';
|
||||
import WorkflowTemplatePopup from "./WorkflowTemplatePopup.jsx";
|
||||
|
||||
import {
|
||||
Badge,
|
||||
@@ -57,7 +58,7 @@ import {
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const EditWorkflow = (props) => {
|
||||
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props
|
||||
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, } = props
|
||||
|
||||
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
||||
|
||||
@@ -143,7 +144,9 @@ const EditWorkflow = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const newWorkflow = isEditing === true ? false : true
|
||||
const newWorkflow = isEditing === true ? false : true
|
||||
const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
console.log("PRIO: ", priority)
|
||||
|
||||
var upload = "";
|
||||
var total_count = 0
|
||||
@@ -161,6 +164,7 @@ const EditWorkflow = (props) => {
|
||||
maxWidth: isMobile ? "90%" : 650,
|
||||
minHeight: 400,
|
||||
paddingTop: 25,
|
||||
paddingLeft: 50,
|
||||
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
},
|
||||
@@ -170,7 +174,7 @@ const EditWorkflow = (props) => {
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 1, color: "rgba(255,255,255,0.9)" }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<Typography variant="h6" style={{flex: 9, }}>
|
||||
<Typography variant="h4" style={{flex: 9, }}>
|
||||
{newWorkflow ? "New" : "Editing"} workflow
|
||||
</Typography>
|
||||
{newWorkflow === true ? null :
|
||||
@@ -193,7 +197,7 @@ const EditWorkflow = (props) => {
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 20, maxWidth: 440,}}>
|
||||
Workflows can be built from scratch, or from templates. <a href="/usecases" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
|
||||
</Typography>
|
||||
{showUpload === true ?
|
||||
@@ -480,7 +484,7 @@ const EditWorkflow = (props) => {
|
||||
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions style={{paddingRight: 20, }}>
|
||||
<DialogActions style={{paddingRight: 100, }}>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
@@ -542,11 +546,38 @@ const EditWorkflow = (props) => {
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Submit"}
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Done"}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
{newWorkflow === true ?
|
||||
<span style={{marginTop: 30, }}>
|
||||
<Typography variant="h6" style={{marginLeft: 30, paddingBottom: 0, }}>
|
||||
Relevant Workflows
|
||||
</Typography>
|
||||
|
||||
{newWorkflow === true && name.length > 5 ?
|
||||
{priority === null || priority === undefined ? null :
|
||||
<div style={{marginLeft: 30, }}>
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
globalUrl={globalUrl}
|
||||
|
||||
srcapp={priority.description.split("&").length > 2 ? priority.description.split("&")[0] : ""}
|
||||
img1={priority.description.split("&").length > 2 ? priority.description.split("&")[1] : ""}
|
||||
|
||||
dstapp={priority.description.split("&").length > 3 ? priority.description.split("&")[2] : ""}
|
||||
img2={priority.description.split("&").length > 3 ? priority.description.split("&")[3] : ""}
|
||||
title={priority.name}
|
||||
description={priority.description.split("&").length > 4 ? priority.description.split("&")[4] : ""}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
</span>
|
||||
: null}
|
||||
|
||||
{newWorkflow === true && name.length > 2 ?
|
||||
<div style={{marginLeft: 30, }}>
|
||||
<WorkflowGrid
|
||||
maxRows={1}
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import 'react-alice-carousel/lib/alice-carousel.css';
|
||||
import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
|
||||
import theme from '../theme.jsx';
|
||||
import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp';
|
||||
import {
|
||||
Checkbox,
|
||||
Button,
|
||||
Collapse,
|
||||
IconButton,
|
||||
FormGroup,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
FormLabel,
|
||||
FormControlLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Grid,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField,
|
||||
Zoom,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
Divider,
|
||||
Tooltip,
|
||||
Chip,
|
||||
ButtonGroup,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
|
||||
const ExploreWorkflow = (props) => {
|
||||
const { userdata, globalUrl } = props
|
||||
const [activeUsecases, setActiveUsecases] = useState(0);
|
||||
const [modalOpen, setModalOpen] = React.useState(false);
|
||||
const [suggestedUsecases, setSuggestedUsecases] = useState([])
|
||||
const [usecasesSet, setUsecasesSet] = useState(false)
|
||||
const [apps, setApps] = useState([])
|
||||
const sizing = 475
|
||||
|
||||
let navigate = useNavigate();
|
||||
|
||||
const imagestyle = {
|
||||
height: 40,
|
||||
borderRadius: 40,
|
||||
//border: "2px solid rgba(255,255,255,0.3)",
|
||||
}
|
||||
|
||||
const loadApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson === null) {
|
||||
console.log("null-response from server")
|
||||
const pretend_apps = [{
|
||||
"name": "TBD",
|
||||
"app_name": "TBD",
|
||||
"app_version": "TBD",
|
||||
"description": "TBD",
|
||||
"version": "TBD",
|
||||
"large_image": "",
|
||||
}]
|
||||
|
||||
setApps(pretend_apps)
|
||||
return
|
||||
}
|
||||
|
||||
if (responseJson.success === false) {
|
||||
console.log("error loading apps: ", responseJson)
|
||||
return
|
||||
}
|
||||
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
// Find priorities in userdata.priorities and check if the item.type === "usecase"
|
||||
// If so, set the item.isActive to true
|
||||
if (usecasesSet === false && userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && suggestedUsecases.length === 0) {
|
||||
|
||||
var tmpUsecases = []
|
||||
for (let i = 0; i < userdata.priorities.length; i++) {
|
||||
if (userdata.priorities[i].type !== "usecase" || userdata.priorities[i].active === false) {
|
||||
continue
|
||||
}
|
||||
|
||||
tmpUsecases.push(userdata.priorities[i])
|
||||
}
|
||||
|
||||
setSuggestedUsecases(tmpUsecases)
|
||||
setUsecasesSet(true)
|
||||
loadApps()
|
||||
}
|
||||
|
||||
const modalView = (
|
||||
// console.log("key:", dataValue.key),
|
||||
//console.log("value:",dataValue.value),
|
||||
<Dialog
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{}}>
|
||||
<div style={{ color: "white", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Sign Up</span>
|
||||
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginLeft: 8, marginRight: 8 }} />
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Setup</span>
|
||||
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginRight: 8 }} />
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Explore</span>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<Typography style={{ fontSize: 16, width: 252, marginLeft: 167 }}>
|
||||
Here’s a recommended workflow:
|
||||
</Typography>
|
||||
{/* <div style={{ marginTop: 0, maxWidth: 700, minWidth: 700, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<div className="thumbs" style={{ display: "flex" }}>
|
||||
<Tooltip title={"Previous usecase"}>
|
||||
<IconButton
|
||||
style={{
|
||||
// backgroundColor: thumbIndex === 0 ? "inherit" : "white",
|
||||
zIndex: 5000,
|
||||
minHeight: 50,
|
||||
maxHeight: 50,
|
||||
color: "grey",
|
||||
marginTop: 150,
|
||||
borderRadius: 50,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}
|
||||
onClick={() => {
|
||||
slidePrev()
|
||||
}}
|
||||
>
|
||||
<ArrowBackIosNewIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
|
||||
<AliceCarousel
|
||||
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
|
||||
items={formattedCarousel}
|
||||
activeIndex={thumbIndex}
|
||||
infiniteLoop
|
||||
mouseTracking={false}
|
||||
responsive={responsive}
|
||||
// activeIndex={activeIndex}
|
||||
controlsStrategy="responsive"
|
||||
autoPlay={false}
|
||||
infinite={true}
|
||||
animationType="fadeout"
|
||||
animationDuration={800}
|
||||
disableButtonsControls
|
||||
|
||||
/>
|
||||
</div>
|
||||
<Tooltip title={"Next usecase"}>
|
||||
<IconButton
|
||||
style={{
|
||||
backgroundColor: thumbIndex === usecaseButtons.length - 1 ? "inherit" : "white",
|
||||
zIndex: 5000,
|
||||
minHeight: 50,
|
||||
maxHeight: 50,
|
||||
color: "grey",
|
||||
marginTop: 150,
|
||||
borderRadius: 50,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}
|
||||
onClick={() => {
|
||||
slideNext()
|
||||
}}
|
||||
>
|
||||
<ArrowForwardIosIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}>
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => setModalOpen(false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => {
|
||||
console.log("hello")
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||
{modalView}
|
||||
<Typography variant="h4" style={{ marginLeft: 8, marginTop: 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)">
|
||||
Start using workflows
|
||||
</Typography>
|
||||
<Typography variant="body2" style={{ marginLeft: 8, marginTop: 10, marginRight: 30, marginBottom: 40, }} color="rgba(158, 158, 158, 1)">
|
||||
Based on what you selected here’s our recommendations!
|
||||
</Typography>
|
||||
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<div className="thumbs" style={{ display: "flex" }}>
|
||||
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
|
||||
<Grid item xs={11} style={{}}>
|
||||
{suggestedUsecases.length === 0 && usecasesSet ?
|
||||
<Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)">
|
||||
All Workflows are already added for your current apps!
|
||||
</Typography>
|
||||
:
|
||||
suggestedUsecases.map((priority, index) => {
|
||||
|
||||
const srcapp = priority.description.split("&")[0]
|
||||
var image1 = priority.description.split("&")[1]
|
||||
var image2 = ""
|
||||
var dstapp = ""
|
||||
if (priority.description.split("&").length > 3) {
|
||||
dstapp = priority.description.split("&")[2]
|
||||
image2 = priority.description.split("&")[3]
|
||||
}
|
||||
|
||||
const name = priority.name.replace("Suggested Usecase: ", "")
|
||||
|
||||
var description = ""
|
||||
if (priority.description.split("&").length > 4) {
|
||||
description = priority.description[4]
|
||||
}
|
||||
|
||||
// FIXME: Should have a proper description
|
||||
description = ""
|
||||
|
||||
return (
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
globalUrl={globalUrl}
|
||||
img1={image1}
|
||||
srcapp={srcapp}
|
||||
img2={image2}
|
||||
dstapp={dstapp}
|
||||
title={name}
|
||||
description={description}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
<div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
|
||||
<Button variant="contained" type="submit"
|
||||
fullWidth style={{
|
||||
borderRadius: 200,
|
||||
height: 51,
|
||||
width: 464,
|
||||
fontSize: 16,
|
||||
padding: "16px 24px",
|
||||
margin: "auto",
|
||||
itemAlign: "center",
|
||||
background: activeUsecases === 0 ? "rgba(47, 47, 47, 1)" : "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
|
||||
color: activeUsecases === 0? "rgba(158, 158, 158, 1)" : "rgba(241, 241, 241, 1)",
|
||||
border: activeUsecases === 0 ? "1px solid rgba(158, 158, 158, 1)" : "none",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/workflows?message="+activeUsecases+" workflows added")
|
||||
}}>
|
||||
Continue to workflows
|
||||
</Button>
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
|
||||
<Link style={{ color: "#f86a3e", marginLeft: 145 }} to="/usecases" className="btn btn-primary">
|
||||
Explore usecases
|
||||
</Link>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ExploreWorkflow
|
||||
@@ -212,7 +212,8 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
|
||||
const notificationWidth = 300
|
||||
const imagesize = 22;
|
||||
const boxColor = "#86c142";
|
||||
const boxColor = "#86c142";
|
||||
|
||||
const NotificationItem = (props) => {
|
||||
const {data} = props
|
||||
|
||||
@@ -263,7 +264,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
{data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ?
|
||||
<Link to={data.reference_url} style={{color: "#f86a3e", textDecoration: "none",}}>
|
||||
<Typography variant="body1">
|
||||
{data.title}
|
||||
{data.title} ({data.amount})
|
||||
</Typography >
|
||||
</Link>
|
||||
:
|
||||
@@ -277,7 +278,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
:
|
||||
null
|
||||
}
|
||||
<Typography variant="body2">
|
||||
<Typography variant="body2" style={{maxHeight: 200, overflowX: "hidden", overflowY: "auto", }}>
|
||||
{data.description}
|
||||
</Typography >
|
||||
{/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ?
|
||||
|
||||
@@ -2278,30 +2278,30 @@ const ParsedAction = (props) => {
|
||||
handleItemClick([innerdata]);
|
||||
}}
|
||||
>
|
||||
<Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-455, padding: "10px 0px 10px 10px", backgroundColor: theme.palette.inputColor, overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
|
||||
<MenuItem
|
||||
key={innerdata.name}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
marginLeft: 15,
|
||||
color: "white",
|
||||
minWidth: 250,
|
||||
maxWidth: 250,
|
||||
padding: 0,
|
||||
position: "relative",
|
||||
}}
|
||||
value={innerdata}
|
||||
onMouseOver={() => {
|
||||
//console.log("HOVER: ", pathdata);
|
||||
}}
|
||||
onClick={() => {
|
||||
handleItemClick([innerdata]);
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" style={{paddingBottom: 5}}>
|
||||
{innerdata.name}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-455, padding: "10px 0px 10px 10px", overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
|
||||
<MenuItem
|
||||
key={innerdata.name}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
marginLeft: 15,
|
||||
color: "white",
|
||||
minWidth: 250,
|
||||
maxWidth: 250,
|
||||
padding: 0,
|
||||
position: "relative",
|
||||
}}
|
||||
value={innerdata}
|
||||
onMouseOver={() => {
|
||||
//console.log("HOVER: ", pathdata);
|
||||
}}
|
||||
onClick={() => {
|
||||
handleItemClick([innerdata]);
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" style={{paddingBottom: 5}}>
|
||||
{innerdata.name}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
{parsedPaths.map((pathdata, index) => {
|
||||
// FIXME: Should be recursive in here
|
||||
//<VpnKeyIcon style={iconStyle} />
|
||||
|
||||
@@ -69,7 +69,7 @@ const Priority = (props) => {
|
||||
|
||||
|
||||
return (
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: 100, maxHeight: 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{flex: 2, overflow: "hidden",}}>
|
||||
<span style={{display: "flex", }}>
|
||||
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null}
|
||||
|
||||
@@ -26,22 +26,21 @@ import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx";
|
||||
import {
|
||||
FullscreenExit as FullscreenExitIcon,
|
||||
Extension as ExtensionIcon,
|
||||
Apps as AppsIcon,
|
||||
FavoriteBorder as FavoriteBorderIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
FormatListNumbered as FormatListNumberedIcon,
|
||||
Apps as AppsIcon,
|
||||
FavoriteBorder as FavoriteBorderIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
FormatListNumbered as FormatListNumberedIcon,
|
||||
SquareFoot as SquareFootIcon,
|
||||
Circle as CircleIcon,
|
||||
Add as AddIcon,
|
||||
Circle as CircleIcon,
|
||||
Add as AddIcon,
|
||||
PlayArrow as PlayArrowIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import {
|
||||
AutoFixHigh as AutoFixHighIcon,
|
||||
Close as CloseIcon,
|
||||
CompressOutlined,
|
||||
QrCodeScannerOutlined,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
import { validateJson } from "../views/Workflows.jsx";
|
||||
import ReactJson from "react-json-view";
|
||||
import PaperComponent from "../components/PaperComponent.jsx";
|
||||
@@ -823,10 +822,6 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
const executeSingleAction = (inputdata) => {
|
||||
//if (serverside === true) {
|
||||
// return
|
||||
//}
|
||||
|
||||
if (validation === true) {
|
||||
inputdata = JSON.stringify(inputdata)
|
||||
}
|
||||
@@ -834,7 +829,10 @@ const CodeEditor = (props) => {
|
||||
// Shuffle Tools 1.2.0 (in most cases?)
|
||||
const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a"
|
||||
|
||||
const actiondata = {"description":"Repeats the call parameter","id":"","name":"repeat_back_to_me","label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters":[{"description":"The message to repeat","id":"","name":"call","example":"REPEATING: Hello world","value":inputdata,"multiline":true,"options":null,"action_field":"","variant":"STATIC_VALUE","required":true,"configuration":false,"tags":null,"schema":{"type":"string"},"skip_multicheck":false,"value_replace":null,"unique_toggled":false,"autocompleted":false}],"execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}}
|
||||
const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : "repeat_back_to_me"
|
||||
const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : [{"name":"call", "value": inputdata}]
|
||||
|
||||
const actiondata = {"description":"Repeats the call parameter","id":"","name":actionname,"label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters": params, "execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}}
|
||||
|
||||
setExecutionResult({
|
||||
"valid": false,
|
||||
@@ -924,6 +922,20 @@ const CodeEditor = (props) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
zIndex: 5000,
|
||||
position: "absolute",
|
||||
top: 14,
|
||||
right: 18,
|
||||
color: "grey",
|
||||
}}
|
||||
onClick={() => {
|
||||
setExpansionModalOpen(false)
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 1, }}>
|
||||
{ isFileEditor ?
|
||||
@@ -1555,7 +1567,7 @@ const CodeEditor = (props) => {
|
||||
<IconButton disabled={executing} color="primary" style={{border: `1px solid ${theme.palette.primary.main}`, marginLeft: 100, padding: 8}} variant="contained" onClick={() => {
|
||||
executeSingleAction(expOutput)
|
||||
}}>
|
||||
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
|
||||
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' or 'execute python' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
|
||||
{executing ? <CircularProgress style={{height: 18, width: 18, }} /> : <PlayArrowIcon style={{height: 18, width: 18, }} /> }
|
||||
|
||||
</Tooltip>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -154,7 +154,6 @@ const AppGrid = props => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success !== false) {
|
||||
console.log("Usecases: ", responseJson)
|
||||
//handleKeysetting(responseJson, workflows)
|
||||
}
|
||||
})
|
||||
@@ -187,10 +186,10 @@ const AppGrid = props => {
|
||||
}, [])
|
||||
|
||||
if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) {
|
||||
console.log("In refinement: ", inputsearch)
|
||||
//setLocalMessage(inputsearch)
|
||||
refine(inputsearch)
|
||||
defaultSearch = inputsearch
|
||||
return null
|
||||
} else if (onlyResults === true) {
|
||||
// Don't return anything unless refinement works
|
||||
return null
|
||||
@@ -244,9 +243,7 @@ const AppGrid = props => {
|
||||
return (
|
||||
<div>
|
||||
{onlyResults === true && hits.length > 0 ?
|
||||
<Typography variant="h6" style={{paddingBottom: 0, }}>
|
||||
Relevant Workflows
|
||||
</Typography>
|
||||
null
|
||||
: null}
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{hits.map((data, index) => {
|
||||
|
||||
@@ -277,7 +277,7 @@ const WorkflowPaper = (props) => {
|
||||
>
|
||||
{data.tags !== undefined && data.tags !== null
|
||||
? data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
if (index >= 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { toast } from "react-toastify"
|
||||
import theme from '../theme.jsx';
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Drawer,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Check as CheckIcon,
|
||||
TrendingFlat as TrendingFlatIcon,
|
||||
Close as CloseIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
|
||||
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
|
||||
|
||||
const WorkflowTemplatePopup = (props) => {
|
||||
const { userdata, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps } = props;
|
||||
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const [workflowLoading, setWorkflowLoading] = useState(false);
|
||||
const [workflow, setWorkflow] = useState({});
|
||||
|
||||
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
|
||||
const imagestyleWrapper = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
}
|
||||
|
||||
const imagestyleWrapperDefault = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid red",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
}
|
||||
|
||||
const imagestyle = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
overflow: "hidden",
|
||||
}
|
||||
|
||||
const imagestyleDefault = {
|
||||
display: "block",
|
||||
marginLeft: 9,
|
||||
marginTop: 10,
|
||||
}
|
||||
|
||||
if (title === undefined || title === null || title === "") {
|
||||
console.log("No title for workflow template popup!");
|
||||
return null
|
||||
}
|
||||
|
||||
const getWorkflow = (workflowId) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/${workflowId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
console.log("Error in workflow loading for ID ", workflowId)
|
||||
} else {
|
||||
setWorkflow(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const loadAppAuth = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps/authentication`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting app auth :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
toast("Failed to get app auth: " + responseJson.reason);
|
||||
return
|
||||
}
|
||||
|
||||
var newauth = [];
|
||||
for (let authkey in responseJson.data) {
|
||||
if (responseJson.data[authkey].defined === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newauth.push(responseJson.data[authkey]);
|
||||
}
|
||||
|
||||
setAppAuthentication(newauth);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
console.log("New auth error: ", error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const getGeneratedWorkflow = () => {
|
||||
// POST
|
||||
// https://shuffler.io/api/v1/workflows/merge
|
||||
// destination: {app_id: "b9c2feaf99b6309dabaeaa8518c61d3d", app_name: "Servicenow_API", app_version: "",…}
|
||||
// id: ""
|
||||
// middle:[]
|
||||
// name: "Email analysis"
|
||||
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
|
||||
|
||||
setWorkflowLoading(true)
|
||||
|
||||
|
||||
// FIXME: Remove hardcoding here after testing, and user srcapp/dstapp
|
||||
const newsrcapp = srcapp
|
||||
const newdstapp = dstapp
|
||||
|
||||
const mergedata = {
|
||||
name: title,
|
||||
id: "",
|
||||
source: {
|
||||
app_name: newsrcapp,
|
||||
},
|
||||
middle: [],
|
||||
destination: {
|
||||
app_name: newdstapp,
|
||||
},
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/workflows/merge", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(mergedata),
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
setWorkflowLoading(false)
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
console.log("Error in workflow template: ", responseJson.error);
|
||||
|
||||
setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.")
|
||||
|
||||
setIsActive(true)
|
||||
//setTimeout(() => {
|
||||
// setModalOpen(false)
|
||||
//}, 5000)
|
||||
} else {
|
||||
console.log("Success in workflow template: ", responseJson);
|
||||
setIsActive(true)
|
||||
if (responseJson.workflow_id === "") {
|
||||
console.log("Failed to build workflow for these tools. Closing in 3 seconds.")
|
||||
return
|
||||
}
|
||||
|
||||
getWorkflow(responseJson.workflow_id)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const isFinished = () => {
|
||||
// Look for configuration fields being done in the current modal
|
||||
// 1. Start by finding the modal
|
||||
const template = document.getElementById("workflow-template")
|
||||
if (template === null || template == undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Find item in template with id app-config
|
||||
const appconfig = template.getElementsByClassName("app-config")
|
||||
if (appconfig === null || appconfig == undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
console.log("APPCONFIG: ", appconfig)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const ModalView = () => {
|
||||
return (
|
||||
<Drawer
|
||||
anchor={"left"}
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: "black",
|
||||
color: "white",
|
||||
minWidth: 700,
|
||||
maxWidth: 700,
|
||||
paddingTop: 75,
|
||||
itemAlign: "center",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
zIndex: 5000,
|
||||
position: "absolute",
|
||||
top: 14,
|
||||
right: 14,
|
||||
color: "white",
|
||||
}}
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent style={{marginTop: 0, marginLeft: 75, maxWidth: 470, }}>
|
||||
<Typography variant="h4">
|
||||
<b>Configure Workflow</b>
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}>
|
||||
Selected Workflow:
|
||||
</Typography>
|
||||
<div style={{marginBottom: 0, }} id="workflow-template">
|
||||
<WorkflowTemplatePopup2
|
||||
globalUrl={globalUrl}
|
||||
img1={img1}
|
||||
srcapp={srcapp}
|
||||
img2={img2}
|
||||
dstapp={dstapp}
|
||||
title={title}
|
||||
description={description}
|
||||
visualOnly={true}
|
||||
/>
|
||||
</div>
|
||||
{workflowLoading ?
|
||||
<div style={{marginTop: 75, textAlign: "center", }}>
|
||||
<Typography variant="h4"> Generating the Workflow...
|
||||
</Typography>
|
||||
<CircularProgress style={{marginLeft: 125, marginTop: 10, }}/>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<Typography variant="h6" style={{marginTop: 75, }}>
|
||||
{errorMessage !== "" ? errorMessage : ""}
|
||||
</Typography>
|
||||
</div>
|
||||
}
|
||||
<ConfigureWorkflow
|
||||
userdata={userdata}
|
||||
theme={theme}
|
||||
globalUrl={globalUrl}
|
||||
workflow={workflow}
|
||||
appAuthentication={appAuthentication}
|
||||
setAppAuthentication={setAppAuthentication}
|
||||
apps={apps}
|
||||
/>
|
||||
{errorMessage === "" ?
|
||||
<Button
|
||||
style={{marginTop: 50, }}
|
||||
variant={isFinished() ? "contained" : "outlined"}
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
: null}
|
||||
</DialogContent>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
var parsedTitle = title
|
||||
const maxlength = 30
|
||||
if (title.length > maxlength) {
|
||||
parsedTitle = title.substring(0, maxlength) + "..."
|
||||
}
|
||||
|
||||
parsedTitle = parsedTitle.replaceAll("_", " ")
|
||||
|
||||
const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : ""
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", maxWidth: 470, minWidth: 470, height: 78, borderRadius: 8 }}>
|
||||
<ModalView />
|
||||
<div
|
||||
// variant={isActive === 1 ? "contained" : "outlined"}
|
||||
color="secondary"
|
||||
disabled={visualOnly === true}
|
||||
style={{
|
||||
margin: 4,
|
||||
width: "100%",
|
||||
borderRadius: 8,
|
||||
textTransform: "none",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
border: isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? "1px solid #f85a3e" : "1px solid rgba(33, 33, 33, 1)",
|
||||
cursor: isActive ? errorMessage !== "" ? "not-allowed" : "pointer" : "pointer",
|
||||
padding: "10px 20px 10px 20px",
|
||||
position: "relative",
|
||||
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setIsHovered(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setIsHovered(false)
|
||||
}}
|
||||
onClick={() => {
|
||||
if (visualOnly === true) {
|
||||
console.log("Not showing more than visuals.")
|
||||
return
|
||||
}
|
||||
|
||||
//setIsActive(!isActive)
|
||||
if (errorMessage !== "") {
|
||||
toast("Already failed to generate workflow for these apps. Please try again later or contact support@shuffler.io.")
|
||||
} else if (isActive) {
|
||||
toast("Workflow already generated. Please try another workflow template!")
|
||||
|
||||
// FIXME: Remove these?
|
||||
loadAppAuth()
|
||||
setModalOpen(true)
|
||||
//getGeneratedWorkflow()
|
||||
} else {
|
||||
|
||||
loadAppAuth()
|
||||
setModalOpen(true)
|
||||
getGeneratedWorkflow()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", itemAlign: "left", textAlign: "left", }}>
|
||||
<div style={{display: "flex", flex: 1, marginTop: 3, }}>
|
||||
{img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ?
|
||||
<Tooltip title={srcapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
|
||||
<span style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
|
||||
<img src={img1} style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleDefault : imagestyle} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
:
|
||||
<span style={{width: 50, }} />
|
||||
}
|
||||
{img2 !== undefined && img2 !== "" && dstapp !== undefined && dstapp !== "" ?
|
||||
<Tooltip title={dstapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
|
||||
<span style={{display: "flex", }}>
|
||||
<TrendingFlatIcon style={{ marginTop: 7, }} />
|
||||
<img src={img2} style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleDefault : imagestyleWrapper} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
:
|
||||
<span style={{width: 50, }} />
|
||||
}
|
||||
</div>
|
||||
<div style={{ flex: 3, marginLeft: 20, }}>
|
||||
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, }} color="rgba(241, 241, 241, 1)">
|
||||
{parsedTitle}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, marginRight: 0, maxHeight: 16, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
|
||||
{parsedDescription}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{isActive === true && errorMessage === "" ?
|
||||
<CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowTemplatePopup
|
||||
@@ -18,10 +18,12 @@ const theme = createTheme(adaptV4Theme({
|
||||
secondary: "rgba(255,255,255,0.7)",
|
||||
},
|
||||
type: "dark",
|
||||
inputColor: "#383B40",
|
||||
inputColor: "rgba(39,41,45,1)",
|
||||
//inputColor: "#383B40",
|
||||
surfaceColor: "#27292d",
|
||||
platformColor: "#1c1c1d",
|
||||
backgroundColor: "#1a1a1a",
|
||||
green: "#5cc879",
|
||||
borderRadius: 5,
|
||||
defaultBorder: "1px solid rgba(255,255,255,0.3)",
|
||||
jsonTheme: "brewer",
|
||||
@@ -70,11 +72,6 @@ const theme = createTheme(adaptV4Theme({
|
||||
},
|
||||
},
|
||||
overrides: {
|
||||
MuiPaper: {
|
||||
root: {
|
||||
backgroundColor: "#1c1c1d",
|
||||
},
|
||||
},
|
||||
MuiMenu: {
|
||||
list: {
|
||||
backgroundColor: "#27292d",
|
||||
|
||||
@@ -1692,15 +1692,24 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
const userId = user.id;
|
||||
const data = { user_id: userId };
|
||||
|
||||
fetch(globalUrl + "/api/v1/generateapikey", {
|
||||
console.log(user, userdata)
|
||||
|
||||
var fetchdata = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
}
|
||||
|
||||
if (userId === userdata.id) {
|
||||
fetchdata.method = "GET"
|
||||
} else {
|
||||
fetchdata.body = JSON.stringify(data)
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/generateapikey", fetchdata)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||
@@ -1889,8 +1898,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
open={selectedUserModalOpen}
|
||||
onClose={() => {
|
||||
setSelectedUserModalOpen(false);
|
||||
setImage2FA("");
|
||||
setSecret2FA("");
|
||||
|
||||
setImage2FA("");
|
||||
setValue2FA("");
|
||||
setSecret2FA("");
|
||||
setShow2faSetup(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
@@ -2156,7 +2168,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
<Card
|
||||
style={{
|
||||
margin: 4,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minHeight: expanded ? 250 : "inherit",
|
||||
maxHeight: expanded ? 250 : "inherit",
|
||||
@@ -2515,9 +2527,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
</span>
|
||||
/>
|
||||
<Tab
|
||||
disabled={!isCloud}
|
||||
label=<span>
|
||||
Billing
|
||||
Billing (Beta)
|
||||
</span>
|
||||
/>
|
||||
<Tab
|
||||
@@ -3246,10 +3257,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
selectedOrganization.child_orgs !== null &&
|
||||
selectedOrganization.child_orgs.length > 0
|
||||
) {
|
||||
console.log("In here?");
|
||||
var active = [];
|
||||
for (var key in userdata.orgs) {
|
||||
console.log("ORG: ", userdata.orgs[key]);
|
||||
const found =
|
||||
selectedOrganization.child_orgs.find(
|
||||
(item) => item.id === userdata.orgs[key].id
|
||||
@@ -3299,7 +3308,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
) : null;
|
||||
|
||||
const run2FASetup = (data) => {
|
||||
console.log("2fa: ", data, show2faSetup);
|
||||
if (!show2faSetup) {
|
||||
get2faCode(data.id);
|
||||
} else {
|
||||
|
||||
@@ -580,15 +580,39 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const cytoscapeWidth = isMobile ? bodyWidth - leftBarSize : bodyWidth - leftBarSize - 25
|
||||
|
||||
const [elements, setElements] = useState([]);
|
||||
const [loopRunning, setLoopRunning] = useState(false)
|
||||
|
||||
const stop = () => {
|
||||
setLoopRunning(false)
|
||||
}
|
||||
|
||||
const start = () => {
|
||||
setLoopRunning(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("In useeffect for loopRunning: ", loopRunning)
|
||||
if (loopRunning) {
|
||||
const intervalId = setInterval(() => {
|
||||
if (!loopRunning) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
|
||||
fetchUpdates()
|
||||
}, 3000)
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}, [loopRunning])
|
||||
|
||||
// No point going as fast, as the nodes aren't realtime anymore, but bulk updated.
|
||||
// Set it from 2500 to 6000 to reduce overall load
|
||||
const { start, stop } = useInterval({
|
||||
duration: 3000,
|
||||
startImmediate: false,
|
||||
callback: () => {
|
||||
fetchUpdates();
|
||||
},
|
||||
});
|
||||
//const { start, stop } = useInterval({
|
||||
// duration: 3000,
|
||||
// startImmediate: false,
|
||||
// callback: () => {
|
||||
// fetchUpdates();
|
||||
// },
|
||||
//});
|
||||
|
||||
const getAppDocs = (appname, location, version) => {
|
||||
fetch(`${globalUrl}/api/v1/docs/${appname}?location=${location}&version=${version}`, {
|
||||
@@ -924,7 +948,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
};
|
||||
|
||||
const getWorkflowExecution = (id, execution_id) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/${id}/executions`, {
|
||||
fetch(`${globalUrl}/api/v2/workflows/${id}/executions`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -940,11 +964,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) {
|
||||
// FIXME: Sort this by time
|
||||
if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null && responseJson.executions.length > 0) {
|
||||
|
||||
// - means it's opposite
|
||||
const newkeys = sortByKey(responseJson, "-started_at");
|
||||
const newkeys = sortByKey(responseJson.executions, "-started_at");
|
||||
setWorkflowExecutions(newkeys);
|
||||
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
@@ -1021,6 +1044,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
},
|
||||
body: JSON.stringify(executionRequest),
|
||||
credentials: "include",
|
||||
cors: "no-cors",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
@@ -1279,7 +1303,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
};
|
||||
|
||||
const sendStreamRequest = (body) => {
|
||||
console.log("Stream not activated yet.")
|
||||
//console.log("Stream not activated yet.")
|
||||
return
|
||||
|
||||
// Session may be important here huh
|
||||
@@ -15302,6 +15326,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
overflow: "hidden",
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
if (cy == undefined || cy == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var currentnode = cy.getElementById(data.action.id);
|
||||
if (currentnode !== undefined && currentnode !== null && currentnode.length !== 0) {
|
||||
currentnode.addClass("shuffle-hover-highlight");
|
||||
@@ -15314,6 +15342,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
//)
|
||||
}}
|
||||
onMouseOut={() => {
|
||||
if (cy == undefined || cy == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var currentnode = cy.getElementById(data.action.id);
|
||||
if (currentnode.length !== 0) {
|
||||
currentnode.removeClass("shuffle-hover-highlight");
|
||||
|
||||
@@ -228,15 +228,16 @@ const parseCurl = (s) => {
|
||||
// Basically CRUD for each category + special
|
||||
export const appCategories = [
|
||||
{
|
||||
"name": "Communication",
|
||||
"name": "Communication",
|
||||
"color": "#FFC107",
|
||||
"icon": "communication",
|
||||
"action_labels": ["List Messages", "Send Message", "Get Message", "Search messages", "List Attachments", "Get Attachment", "Get Contact"],
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"name": "SIEM",
|
||||
"color": "#FFC107",
|
||||
"icon": "siem",
|
||||
"action_labels": ["Search", "List Alerts", "Close Alert", "Get Alert", "Create detection", "Add to lookup list",],
|
||||
"action_labels": ["Search", "List Alerts", "Close Alert", "Get Alert", "Create detection", "Add to lookup list", "Isolate endpoint",],
|
||||
}, {
|
||||
"name": "Eradication",
|
||||
"color": "#FFC107",
|
||||
|
||||
+78
-10
@@ -287,6 +287,7 @@ const Apps = (props) => {
|
||||
const [selectedAction, setSelectedAction] = React.useState({});
|
||||
const [searchBackend, setSearchBackend] = React.useState(false);
|
||||
const [searchableApps, setSearchableApps] = React.useState([]);
|
||||
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
||||
|
||||
const [openApi, setOpenApi] = React.useState("");
|
||||
const [openApiData, setOpenApiData] = React.useState("");
|
||||
@@ -441,6 +442,7 @@ const Apps = (props) => {
|
||||
if (privateapps.length > 0) {
|
||||
if (selectedApp.id === undefined || selectedApp.id === null) {
|
||||
setSelectedApp(privateapps[0]);
|
||||
setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you")
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -646,6 +648,7 @@ const Apps = (props) => {
|
||||
if (selectedApp.id !== data.id) {
|
||||
data.name = newAppname;
|
||||
setSelectedApp(data);
|
||||
setSharingConfiguration(data.sharing === true ? "public" : "you")
|
||||
|
||||
if (
|
||||
data.actions !== undefined &&
|
||||
@@ -658,7 +661,7 @@ const Apps = (props) => {
|
||||
}
|
||||
|
||||
if (data.sharing) {
|
||||
setSharingConfiguration(isCloud ? "public" : "everyone");
|
||||
setSharingConfiguration("public");
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -999,11 +1002,11 @@ const Apps = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const userRoles = ["you", isCloud ? "public" : "everyone"];
|
||||
const userRoles = ["you", "public"];
|
||||
|
||||
// Admin in org or creator of app
|
||||
// FIXME: Missing check for if same creator account
|
||||
const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated
|
||||
// Admin in org or creator of app
|
||||
// FIXME: Missing check for if same creator account
|
||||
const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated
|
||||
|
||||
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"),
|
||||
var baseInfo =
|
||||
@@ -1054,6 +1057,7 @@ const Apps = (props) => {
|
||||
console.log("New version: ", newversion);
|
||||
selectedApp.app_version = selectedApp.app_version;
|
||||
setSelectedApp(selectedApp);
|
||||
setSharingConfiguration(selectedApp.sharing === true ? "public" : "you")
|
||||
|
||||
if (newversion !== undefined && newversion !== null) {
|
||||
getApp(newversion.id, true);
|
||||
@@ -1156,17 +1160,22 @@ const Apps = (props) => {
|
||||
<Select
|
||||
value={sharingConfiguration}
|
||||
onChange={(event) => {
|
||||
toast("Changing sharing to " + event.target.value);
|
||||
|
||||
setSharingConfiguration(event.target.value);
|
||||
|
||||
if (event.target.value === "you") {
|
||||
toast("Changing sharing to " + event.target.value);
|
||||
updateAppField(selectedApp.id, "sharing", false);
|
||||
} else if (
|
||||
event.target.value === "everyone" ||
|
||||
event.target.value === "public"
|
||||
) {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
|
||||
if (!isCloud) {
|
||||
setPublishModalOpen(true)
|
||||
} else {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
"Can't handle value for sharing: ",
|
||||
@@ -1354,7 +1363,8 @@ const Apps = (props) => {
|
||||
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
|
||||
cursor: hover ? "pointer" : "default",
|
||||
textAlign: "center",
|
||||
height: 125,
|
||||
minHeight: 150,
|
||||
maxHeight: 150,
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
@@ -2246,6 +2256,7 @@ const Apps = (props) => {
|
||||
setSelectedAction({});
|
||||
}
|
||||
setSelectedApp(responseJson);
|
||||
setSharingConfiguration(responseJson.sharing === true ? "public" : "you")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -2281,6 +2292,7 @@ const Apps = (props) => {
|
||||
const data = {};
|
||||
data[fieldname] = fieldvalue;
|
||||
|
||||
|
||||
console.log("DATA: ", data);
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/" + app_id, {
|
||||
@@ -2304,8 +2316,8 @@ const Apps = (props) => {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||
toast("Error: "+responseJson.reason);
|
||||
} else {
|
||||
toast("Error updating app configuration");
|
||||
}
|
||||
toast("Error updating app configuration. Are you the owner of this app?");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -2506,6 +2518,61 @@ const Apps = (props) => {
|
||||
setLoadAppsModalOpen(false);
|
||||
};
|
||||
|
||||
const publishModal = publishModalOpen ? (
|
||||
<Dialog
|
||||
open={publishModalOpen}
|
||||
onClose={() => {
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
padding: 50,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{ marginBottom: 0 }}>
|
||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
|
||||
Are you sure you want to PUBLISH this app?
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent
|
||||
style={{ color: "rgba(255,255,255,0.65)", textAlign: "center" }}
|
||||
>
|
||||
<div>
|
||||
<Typography variant="body1" style={{ marginBottom: 20 }}>
|
||||
Before publishing, make sure to sanitize the App for anything you don't want public.
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{ marginBottom: 20 }}>
|
||||
The published App is yours, and you can always change your public Apps after they are released.
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Yes
|
||||
</Button>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
No
|
||||
</Button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
const deleteModal = deleteModalOpen ? (
|
||||
<Dialog
|
||||
open={deleteModalOpen}
|
||||
@@ -2936,6 +3003,7 @@ const Apps = (props) => {
|
||||
<div>
|
||||
{appView}
|
||||
{modalView}
|
||||
{publishModal}
|
||||
{generateAppView}
|
||||
{appsModalLoad}
|
||||
{deleteModal}
|
||||
|
||||
+376
-156
@@ -6,11 +6,13 @@ import { makeStyles, } from "@mui/styles";
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { parsedDatatypeImages } from "../components/AppFramework.jsx"
|
||||
|
||||
import {
|
||||
Autocomplete,
|
||||
@@ -90,7 +92,7 @@ const useStyles = makeStyles({
|
||||
},
|
||||
});
|
||||
|
||||
const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => {
|
||||
const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => {
|
||||
const [expandedIndex, setExpandedIndex] = useState(-1);
|
||||
const [expandedItem, setExpandedItem] = useState(-1);
|
||||
const [inputUsecase, setInputUsecase] = useState({});
|
||||
@@ -103,17 +105,222 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
|
||||
const [selectedWorkflows, setSelectedWorkflows] = useState([])
|
||||
const [firstLoad, setFirstLoad] = useState(true)
|
||||
const [apps, setApps] = useState([])
|
||||
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [mitreTags, setMitreTags] = useState([]);
|
||||
|
||||
const loadApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson === null) {
|
||||
console.log("null-response from server")
|
||||
const pretend_apps = [{
|
||||
"name": "TBD",
|
||||
"app_name": "TBD",
|
||||
"app_version": "TBD",
|
||||
"description": "TBD",
|
||||
"version": "TBD",
|
||||
"large_image": "",
|
||||
}]
|
||||
|
||||
setApps(pretend_apps)
|
||||
return
|
||||
}
|
||||
|
||||
if (responseJson.success === false) {
|
||||
console.log("error loading apps: ", responseJson)
|
||||
return
|
||||
}
|
||||
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadApps()
|
||||
}, [])
|
||||
|
||||
if (keys === undefined || keys === null || keys.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const getUsecase = (name, index, subindex) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(name.replaceAll(" ", "_"))}`, {
|
||||
const findSpecificApp = (framework, inputcategory) => {
|
||||
// Get the frameworkinfo for the org and fill in
|
||||
//
|
||||
if (framework === undefined || framework === null) {
|
||||
console.log("findSpecificApp: frameworkData is null")
|
||||
return null
|
||||
}
|
||||
|
||||
if (inputcategory === undefined || inputcategory === null) {
|
||||
console.log("findSpecificApp: category is null")
|
||||
return null
|
||||
}
|
||||
|
||||
const category = inputcategory.toLowerCase()
|
||||
|
||||
console.log("findSpecificApp: ", category, frameworkData)
|
||||
if (category === "edr" || category === "eradication" || category === "edr & av") {
|
||||
if (frameworkData["EDR & AV"] !== undefined && frameworkData["EDR & AV"].name !== undefined) {
|
||||
return frameworkData["EDR & AV"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "EDR :default",
|
||||
large_image: parsedDatatypeImages["EDR & AV"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "communication") {
|
||||
if (frameworkData["Comms"] !== undefined && frameworkData["Comms"].name !== undefined) {
|
||||
return frameworkData["Comms"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "COMMS :default",
|
||||
large_image: parsedDatatypeImages["COMMS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "email") {
|
||||
if (frameworkData["Email"] !== undefined && frameworkData["Email"].name !== undefined) {
|
||||
return frameworkData["Email"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "COMMS :default",
|
||||
large_image: parsedDatatypeImages["COMMS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "assets") {
|
||||
if (frameworkData["Assets"] !== undefined && frameworkData["Assets"].name !== undefined) {
|
||||
return frameworkData["Assets"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "ASSETS :default",
|
||||
large_image: parsedDatatypeImages["ASSETS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "cases") {
|
||||
if (frameworkData["Cases"] !== undefined && frameworkData["Cases"].name !== undefined) {
|
||||
return frameworkData["Cases"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "CASES :default",
|
||||
large_image: parsedDatatypeImages["CASES"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "iam") {
|
||||
if (frameworkData["IAM"] !== undefined && frameworkData["IAM"].name !== undefined) {
|
||||
return frameworkData["IAM"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "EDR :default",
|
||||
large_image: parsedDatatypeImages["EDR & AV"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "network") {
|
||||
if (frameworkData["Network"] !== undefined && frameworkData["Network"].name !== undefined) {
|
||||
return frameworkData["Network"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "Network :default",
|
||||
large_image: parsedDatatypeImages["NETWORK"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "intel") {
|
||||
if (frameworkData["Intel"] !== undefined && frameworkData["Intel"].name !== undefined) {
|
||||
return frameworkData["Intel"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "INTEL :default",
|
||||
large_image: parsedDatatypeImages["INTEL"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "siem") {
|
||||
if (frameworkData["SIEM"] !== undefined && frameworkData["SIEM"].name !== undefined) {
|
||||
return frameworkData["SIEM"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "SIEM :default",
|
||||
large_image: parsedDatatypeImages["SIEM"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else {
|
||||
console.log("findSpecificApp: unknown category: ", category)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const parseUsecase = (subcase) => {
|
||||
console.log("parseUsecase: ", subcase)
|
||||
const srcdata = findSpecificApp(frameworkData, subcase.type)
|
||||
const dstdata = findSpecificApp(frameworkData, subcase.last)
|
||||
|
||||
console.log("srcdata: ", srcdata)
|
||||
console.log("dstdata: ", dstdata)
|
||||
|
||||
if (srcdata !== undefined && srcdata !== null) {
|
||||
subcase.srcimg = srcdata.large_image
|
||||
subcase.srcapp = srcdata.name
|
||||
}
|
||||
|
||||
if (dstdata !== undefined && dstdata !== null) {
|
||||
subcase.dstimg = dstdata.large_image
|
||||
subcase.dstapp = dstdata.name
|
||||
}
|
||||
|
||||
return subcase
|
||||
}
|
||||
|
||||
const getUsecase = (subcase, index, subindex) => {
|
||||
subcase = parseUsecase(subcase)
|
||||
|
||||
// Timeout 50ms to delay it slightly
|
||||
setTimeout(() => {
|
||||
setInputUsecase(subcase)
|
||||
}, 50)
|
||||
|
||||
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -121,50 +328,68 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
setInputUsecase({
|
||||
"name": name,
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
console.log("In responseJson for usecase: ", responseJson)
|
||||
var parsedUsecase = responseJson
|
||||
|
||||
if (responseJson.success === false) {
|
||||
|
||||
//img1={inputUsecase.srcimg}
|
||||
//srcapp={inputUsecase.srcapp}
|
||||
//img2={inputUsecase.dstimg}
|
||||
//dstapp={inputUsecase.dstapp}
|
||||
//title={inputUsecase.name}
|
||||
parsedUsecase = subcase
|
||||
} else {
|
||||
parsedUsecase = responseJson
|
||||
|
||||
parsedUsecase.srcimg = subcase.srcimg
|
||||
parsedUsecase.srcapp = subcase.srcapp
|
||||
parsedUsecase.dstimg = subcase.dstimg
|
||||
parsedUsecase.dstapp = subcase.dstapp
|
||||
|
||||
//parsedUsecase = parseUsecase(responseJson)
|
||||
}
|
||||
|
||||
// Look for the type of app and fill in img1, srcapp...
|
||||
console.log("USECASE: ", parsedUsecase)
|
||||
|
||||
setInputUsecase(parsedUsecase)
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setTimeout(() => {
|
||||
//console.log("Scroll!")
|
||||
const found = document.getElementById("selected_box");
|
||||
if (found !== undefined && found !== null) {
|
||||
//console.log("FOUND!!")
|
||||
found.scrollTo({
|
||||
top: 100,
|
||||
behavior: "smooth",
|
||||
})
|
||||
} else {
|
||||
setInputUsecase(responseJson)
|
||||
}
|
||||
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setTimeout(() => {
|
||||
//console.log("Scroll!")
|
||||
const found = document.getElementById("selected_box");
|
||||
if (found !== undefined && found !== null) {
|
||||
//console.log("FOUND!!")
|
||||
found.scrollTo({
|
||||
top: 100,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
})
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
})
|
||||
}
|
||||
|
||||
const setUsecaseItem = (inputUsecase) => {
|
||||
var parsedUsecase = inputUsecase
|
||||
@@ -225,7 +450,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
//toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const setWorkflow = (workflowdata) => {
|
||||
const new_url = `${globalUrl}/api/v1/workflows/${workflowdata.id}`
|
||||
@@ -289,6 +514,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
if (selectedItem && subcase.matches.length > 0 && selectedWorkflows.length === 0 && firstLoad === true) {
|
||||
setFirstLoad(false)
|
||||
setSelectedWorkflows(subcase.matches)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,10 +542,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
}
|
||||
}
|
||||
|
||||
//const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color
|
||||
const finished = subcase.matches.length > 0
|
||||
const backgroundColor = theme.palette.surfaceColor
|
||||
//"inherit"
|
||||
const itemBorder = `${selectedItem ? "3px" : expandedItem >= 0 ? "0px" : "1px"} solid ${usecase.color}`
|
||||
|
||||
const fixedName = subcase.name.toLowerCase().replace("_", " ")
|
||||
@@ -330,14 +554,14 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
//setSelectedWorkflows([])
|
||||
if (selectedItem) {
|
||||
} else {
|
||||
getUsecase(subcase.name, index, subindex)
|
||||
getUsecase(subcase, index, subindex)
|
||||
navigate(`/usecases?selected_object=${fixedName}`)
|
||||
|
||||
//const newitem = removeParam("selected_object", cursearch);
|
||||
//navigate(curpath + newitem)
|
||||
}
|
||||
}}>
|
||||
<Paper style={{padding: 25, minHeight: 75, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||
<Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||
}}>
|
||||
{!selectedItem ?
|
||||
<div style={{textAlign: "left", position: "relative",}}>
|
||||
@@ -649,12 +873,12 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
: null}
|
||||
|
||||
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
|
||||
<Autocomplete
|
||||
multiple
|
||||
<Autocomplete
|
||||
multiple
|
||||
id="workflow_matching"
|
||||
options={workflows}
|
||||
autoHighlight
|
||||
value={selectedWorkflows}
|
||||
value={selectedWorkflows}
|
||||
classes={{ inputRoot: classes.inputRoot }}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
@@ -662,7 +886,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
getOptionSelected={(option, value) => option.id === value.id}
|
||||
getOptionSelected={(option, value) => option.id === value.id}
|
||||
getOptionLabel={(option) => {
|
||||
|
||||
if (
|
||||
@@ -685,120 +909,120 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
console.log("CLICK: ", newValue)
|
||||
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
|
||||
//setSelectedWorkflows=
|
||||
//var newvalue = []
|
||||
//for (var key in newValue) {
|
||||
// if (newValue[key].id !== undefined) {
|
||||
// newvalue.push(newValue[key].id)
|
||||
// }
|
||||
//}
|
||||
console.log("CLICK: ", newValue)
|
||||
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
|
||||
//setSelectedWorkflows=
|
||||
//var newvalue = []
|
||||
//for (var key in newValue) {
|
||||
// if (newValue[key].id !== undefined) {
|
||||
// newvalue.push(newValue[key].id)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Doing this way as you may want to remove some too
|
||||
for (var key in workflows) {
|
||||
if (!newValue.find(data => data.id === workflows[key].id)) {
|
||||
// Check if it has the one in it
|
||||
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
|
||||
if (filtered !== undefined && filtered !== null) {
|
||||
//console.log("Removing: ", workflows[key].name, workflows[key])
|
||||
workflows[key]["usecase_ids"] = filtered
|
||||
|
||||
setWorkflow(workflows[key])
|
||||
}
|
||||
}
|
||||
// Doing this way as you may want to remove some too
|
||||
for (var key in workflows) {
|
||||
if (!newValue.find(data => data.id === workflows[key].id)) {
|
||||
// Check if it has the one in it
|
||||
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
|
||||
if (filtered !== undefined && filtered !== null) {
|
||||
//console.log("Removing: ", workflows[key].name, workflows[key])
|
||||
workflows[key]["usecase_ids"] = filtered
|
||||
|
||||
setWorkflow(workflows[key])
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
|
||||
workflows[key]["usecase_ids"] = [subcase.name]
|
||||
console.log("Setting: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
|
||||
workflows[key]["usecase_ids"] = [subcase.name]
|
||||
console.log("Setting: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
|
||||
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
workflows[key]["usecase_ids"].push(subcase.name)
|
||||
console.log("Adding: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
workflows[key]["usecase_ids"].push(subcase.name)
|
||||
console.log("Adding: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWorkflows(workflows)
|
||||
console.log("New: ", newValue)
|
||||
setSelectedWorkflows(newValue)
|
||||
setWorkflows(workflows)
|
||||
console.log("New: ", newValue)
|
||||
setSelectedWorkflows(newValue)
|
||||
//setUpdate(Math.random())
|
||||
}}
|
||||
renderOption={(props, option) => {
|
||||
//console.log("In options?: ", props, option)
|
||||
renderOption={(props, data, state) => {
|
||||
var newname = data.name
|
||||
if (newname === undefined || newname === null) {
|
||||
newname = "placeholder"
|
||||
}
|
||||
|
||||
var newname = props.name
|
||||
if (newname === undefined || newname === null) {
|
||||
newname = "placeholder"
|
||||
}
|
||||
|
||||
if (newname.length > 2) {
|
||||
newname = newname.charAt(0).toUpperCase() + newname.substring(1)
|
||||
}
|
||||
return (
|
||||
<li {...props}>
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{props.image !== undefined && props.image !== null && props.image.length > 0 ?
|
||||
<img src={props.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<span>
|
||||
<Checkbox
|
||||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
|
||||
checkedIcon={<CheckBoxIcon fontSize="small" />}
|
||||
style={{ marginRight: 8 }}
|
||||
checked={option.selected}
|
||||
/>
|
||||
{newname}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
if (newname.length > 2) {
|
||||
newname = newname.charAt(0).toUpperCase() + newname.substring(1)
|
||||
}
|
||||
return (
|
||||
<li {...props}>
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||
<img src={data.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<span>
|
||||
<Checkbox
|
||||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
|
||||
checkedIcon={<CheckBoxIcon fontSize="small" />}
|
||||
style={{ marginRight: 8 }}
|
||||
checked={selectedWorkflows.find(wf => wf.id === data.id) !== undefined}
|
||||
/>
|
||||
{newname}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
renderInput={(params) => {
|
||||
return (
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find your workflows"
|
||||
variant="outlined"
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find your workflows"
|
||||
variant="outlined"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
: null}
|
||||
: null}
|
||||
<span style={{top: 30, position: "relative",}}>
|
||||
<Typography variant="body1" style={{marginTop: 0,}} onClick={() => {}}>
|
||||
Try this Usecase
|
||||
</Typography>
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
|
||||
globalUrl={globalUrl}
|
||||
img1={inputUsecase.srcimg}
|
||||
srcapp={inputUsecase.srcapp}
|
||||
img2={inputUsecase.dstimg}
|
||||
dstapp={inputUsecase.dstapp}
|
||||
title={inputUsecase.name}
|
||||
description={inputUsecase.description}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
</span>
|
||||
{/*
|
||||
|
||||
{/*subcase.matches.length > 0 ?
|
||||
<Grid container style={{maxWidth: 325, marginTop: 10, }}>
|
||||
{subcase.matches.map((workflow, workflowindex) => {
|
||||
return (
|
||||
<Grid key={workflowindex} item index={workflowindex} xs={12}>
|
||||
<WorkflowPaper key={workflowindex} data={workflow} />
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
:
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflow selected yet.
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
{subcase.extra_buttons !== undefined && subcase.extra_buttons !== null && subcase.extra_buttons.length > 0 ?
|
||||
<div style={{marginTop: 25, }}>
|
||||
@@ -852,15 +1076,9 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
<Typography variant="body1" style={{marginTop: 15, cursor: "pointer",}} onClick={() => {}}>
|
||||
See other Public Workflows for {} <OpenInNewIcon style={{marginTop: 5, marginLeft: 15, }}/>
|
||||
</Typography>
|
||||
{/*
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflows yet.
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
</a>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
}
|
||||
<div style={{
|
||||
@@ -868,6 +1086,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
width: 350,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
padding: 5,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
}}>
|
||||
<AppFramework
|
||||
inputUsecase={inputUsecase}
|
||||
|
||||
@@ -305,7 +305,6 @@ const LoginDialog = (props) => {
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
position: "relative",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
textAlign: "left",
|
||||
marginTop: 15,
|
||||
}}
|
||||
@@ -331,17 +330,25 @@ const LoginDialog = (props) => {
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>1.</b> Make sure shuffle-database folder has correct access:{" "}
|
||||
<b>1.</b> Make sure shuffle-database folder has correct access, and that you have a minimum of <b>2Gb of RAM available</b>:{" "}
|
||||
<br />
|
||||
<br />
|
||||
sudo chown 1000:1000 -R shuffle-database
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>2</b>. Restart docker-compose:
|
||||
<b>2.</b> Disable memory swap on the host
|
||||
<br />
|
||||
<br />
|
||||
sudo swapoff -a
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>3</b>. Restart docker-compose:
|
||||
<br />
|
||||
<br />
|
||||
sudo docker-compose restart
|
||||
@@ -513,7 +520,7 @@ const LoginDialog = (props) => {
|
||||
//id="sso_button"
|
||||
const ssoBtn = document.getElementById("sso_button");
|
||||
if (ssoBtn !== undefined && ssoBtn !== null) {
|
||||
console.log("SSO BTN: ", ssoBtn)
|
||||
//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) {
|
||||
@@ -527,7 +534,7 @@ const LoginDialog = (props) => {
|
||||
}, 200);
|
||||
}, [ssoUrl])
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
return <div style={{paddingBottom: 150, }}>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default LoginDialog;
|
||||
|
||||
@@ -26,7 +26,7 @@ const SetAuthentication = (props) => {
|
||||
//const alert = useAlert();
|
||||
|
||||
const parseIncomingOpenapiData = (data) => {
|
||||
if (data.app === undefined || data.app === null) {
|
||||
if (data.app === undefined || data.app === null) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ const SetAuthentication = (props) => {
|
||||
parsedapp.name = parsedapp.name.replaceAll("_", " ");
|
||||
setApp(parsedapp);
|
||||
|
||||
document.title = parsedapp.name + " App Auth";
|
||||
document.title = parsedapp.name + " App Auth";
|
||||
|
||||
}
|
||||
|
||||
console.log("App: ", app)
|
||||
console.log("App: ", app)
|
||||
|
||||
const getApp = (appid) => {
|
||||
if (serverside === true) {
|
||||
@@ -64,21 +64,21 @@ const SetAuthentication = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_not_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_not_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return response.json();
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||
import Drift from "react-driftjs";
|
||||
|
||||
const Welcome = (props) => {
|
||||
@@ -301,44 +302,47 @@ const Welcome = (props) => {
|
||||
flex: 1,
|
||||
padding: 0,
|
||||
textAlign: "center",
|
||||
maxWidth: 300,
|
||||
minWidth: 300,
|
||||
maxWidth: 275,
|
||||
minWidth: 275,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}
|
||||
|
||||
const actionObject = {
|
||||
padding: "35px",
|
||||
maxHeight: 300,
|
||||
minHeight: 300,
|
||||
padding: "25px",
|
||||
maxHeight: 280,
|
||||
minHeight: 280,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}
|
||||
|
||||
const imageStyle = {
|
||||
width: 150,
|
||||
width: 70,
|
||||
// height: 150,
|
||||
// margin: "auto",
|
||||
// marginTop: 10,
|
||||
borderRadius: 75,
|
||||
marginBottom: 18,
|
||||
// borderRadius: 75,
|
||||
objectFit: "scale-down",
|
||||
}
|
||||
const buttonStyle = {
|
||||
borderRadius: 8,
|
||||
borderRadius: 200,
|
||||
height: 51,
|
||||
width: 464,
|
||||
fontSize: 16,
|
||||
background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
|
||||
// background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
|
||||
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
|
||||
padding: "16px 24px",
|
||||
top: 75,
|
||||
top: 105,
|
||||
margin: "auto",
|
||||
itemAlign: "center",
|
||||
marginLeft: "65px",
|
||||
}
|
||||
|
||||
const defaultImage = "/images/experienced.png"
|
||||
const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage
|
||||
return (
|
||||
<div style={{width: 1000, margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}>
|
||||
<div style={{ margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}>
|
||||
{/*
|
||||
<div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}>
|
||||
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
|
||||
@@ -346,7 +350,7 @@ const Welcome = (props) => {
|
||||
*/}
|
||||
{showWelcome === true ?
|
||||
<div>
|
||||
<div style={{minWidth: 500, maxWidth: 500, margin: "auto", marginTop: isCloud ? "auto" : 20, }}>
|
||||
{/* <div style={{minWidth: 500, maxWidth: 500, margin: "auto", marginTop: isCloud ? "auto" : 20, }}>
|
||||
<Stepper
|
||||
activeStep={activeStep}
|
||||
color="primary"
|
||||
@@ -379,8 +383,8 @@ const Welcome = (props) => {
|
||||
)
|
||||
})}
|
||||
</Stepper>
|
||||
</div>
|
||||
<Grid container spacing={2} style={{ padding: 0, maxWidth: 1000, minWidth: 1000, margin: "auto", }}>
|
||||
</div> */}
|
||||
<Grid container spacing={1} style={{ padding: 0, maxWidth: 500, minWidth: 500, marginRight : "auto" , marginLeft: "auto" }}>
|
||||
<Grid item xs={window.location.href.includes("tab=2") ? 6 : 12}>
|
||||
<div>
|
||||
{/*
|
||||
@@ -414,7 +418,7 @@ const Welcome = (props) => {
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
{frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null :
|
||||
{/* {frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null :
|
||||
<div style={{marginTop: 25, }}>
|
||||
<Typography variant="h6" style={{textAlign: "center", marginBottom: 25, }}>
|
||||
App Framework
|
||||
@@ -439,32 +443,35 @@ const Welcome = (props) => {
|
||||
/>
|
||||
</Fade>
|
||||
</div>
|
||||
}
|
||||
} */}
|
||||
</Grid>
|
||||
</div>
|
||||
:
|
||||
<Fade in={true}>
|
||||
<div style={{maxWidth: 700, margin: "auto", marginTop: 50, }}>
|
||||
{/*
|
||||
<div style={{display:"flex"}}>
|
||||
<ArrowBackIosIcon style={{color: "#9E9E9E",}} onClick={() => {
|
||||
navigate("/login")
|
||||
<div style={{maxWidth: 590, margin: "auto", marginTop: 50, }}>
|
||||
|
||||
{/* <div style={{display:"flex"}}>
|
||||
<ArrowBackIosIcon style={{color: "#9E9E9E", width: "16px",}} onClick={() => {
|
||||
navigate("/workflows")
|
||||
}}/>
|
||||
<Typography variant="body1" style={{color: "#9E9E9E",textAlign: "center", marginBottom: 50, paddingRight: "366px"}} onClick={() => {
|
||||
navigate("/login")
|
||||
<Typography variant="h8" style={{color: "#9E9E9E",textAlign: "center", marginBottom: 20, paddingRight: "366px"}} onClick={() => {
|
||||
navigate("/workflows")
|
||||
}}>
|
||||
Back
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
<Typography variant="h4" style={{color: "#F1F1F1", textAlign: "center", marginTop: 50, }}>
|
||||
</div> */}
|
||||
|
||||
<Typography variant="h4" style={{color: "#F1F1F1", marginTop: 50, fontSize: 32}}>
|
||||
Help us get to know you
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{color: "#9E9E9E", textAlign: "center", marginBottom: 50,}}>
|
||||
We will use this information to personalize your automation
|
||||
<Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}>
|
||||
Let us help you create a smoother journey.
|
||||
</Typography>
|
||||
{/* <Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}>
|
||||
We will use this information to personalize your automation
|
||||
</Typography> */}
|
||||
<div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}>
|
||||
<div style={{border: "1px solid #806BFF", borderRadius: theme.palette.borderRadius, }}>
|
||||
<div style={{border: "2px solid #806BFF", borderRadius: theme.palette.borderRadius, }}>
|
||||
<Card style={paperObject} onClick={() => {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
@@ -506,7 +513,7 @@ const Welcome = (props) => {
|
||||
navigate("/workflows?message=Skipped intro")
|
||||
}}>
|
||||
<CardActionArea style={actionObject}>
|
||||
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 38 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius, }} />
|
||||
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius, marginBottom: experienced_image === defaultImage ? 24 : 2 }} />
|
||||
<Typography variant="h4" style={{color: "#F1F1F1"}}>
|
||||
Experienced
|
||||
</Typography>
|
||||
@@ -517,17 +524,22 @@ const Welcome = (props) => {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
<div style={{display: "flex", flexDirection: "row", }}>
|
||||
|
||||
<div style={{ flexDirection: "row", }}>
|
||||
<Button variant="contained" type="submit" fullWidth style={buttonStyle} onClick={() => {
|
||||
navigate("/workflows?message=Skipped intro continue")
|
||||
ReactGA.event({
|
||||
category: "welcome",
|
||||
action: "click_welcome_continue",
|
||||
label: "",
|
||||
})
|
||||
}}>
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
|
||||
<div style={{margin: "auto", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
|
||||
|
||||
{/* <div style={{margin: "auto", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
|
||||
if (window.drift !== undefined) {
|
||||
window.drift.api.startInteraction({ interactionId: 340045 })
|
||||
} else {
|
||||
@@ -537,7 +549,7 @@ const Welcome = (props) => {
|
||||
<Typography variant="body1" style={{margin: "auto", textAlign: "center"}}>
|
||||
Want a demo instead?
|
||||
</Typography>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</Fade>
|
||||
}
|
||||
|
||||
+129
-139
@@ -11,13 +11,13 @@ import { isMobile } from "react-device-detect"
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Divider,
|
||||
Divider,
|
||||
Avatar,
|
||||
Drawer,
|
||||
Grid,
|
||||
InputLabel,
|
||||
Select,
|
||||
ListSubheader,
|
||||
InputLabel,
|
||||
Select,
|
||||
ListSubheader,
|
||||
Paper,
|
||||
Tooltip,
|
||||
Button,
|
||||
@@ -533,7 +533,6 @@ const Workflows = (props) => {
|
||||
document.title = "Shuffle - Workflows";
|
||||
let navigate = useNavigate();
|
||||
|
||||
//const alert = useAlert();
|
||||
const classes = useStyles(theme);
|
||||
const imgSize = 60;
|
||||
|
||||
@@ -590,6 +589,9 @@ const Workflows = (props) => {
|
||||
const [drawerOpen, setDrawerOpen] = React.useState(false)
|
||||
const [videoViewOpen, setVideoViewOpen] = React.useState(false)
|
||||
const [gettingStartedItems, setGettingStartedItems] = React.useState([])
|
||||
const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([])
|
||||
|
||||
const [apps, setApps] = React.useState([]);
|
||||
|
||||
const drawerWidth = drawerOpen ? 325 : 0
|
||||
|
||||
@@ -725,6 +727,30 @@ const Workflows = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for apps :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: "+error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const addFilter = (data) => {
|
||||
if (data === null || data === undefined) {
|
||||
console.log("No filter data")
|
||||
@@ -891,13 +917,15 @@ const Workflows = (props) => {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
padding: 50,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
|
||||
Are you sure you want to delete this workflow? <div />
|
||||
Other workflows relying on this one may stop working
|
||||
Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}? <div />
|
||||
|
||||
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent
|
||||
@@ -912,7 +940,21 @@ const Workflows = (props) => {
|
||||
setTimeout(() => {
|
||||
getAvailableWorkflows();
|
||||
}, 1000);
|
||||
}
|
||||
} else if (selectedWorkflowIndexes.length > 0) {
|
||||
// Do backwards so it doesn't change
|
||||
for (var i = selectedWorkflowIndexes.length - 1; i >= 0; i--) {
|
||||
const workflow = filteredWorkflows[selectedWorkflowIndexes[i]-1]
|
||||
if (workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null) {
|
||||
deleteWorkflow(workflow.id);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
getAvailableWorkflows();
|
||||
}, 1000);
|
||||
|
||||
setSelectedWorkflowIndexes([]);
|
||||
}
|
||||
setDeleteModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
@@ -1115,9 +1157,9 @@ const Workflows = (props) => {
|
||||
|
||||
// Ensures the zooming happens only once per load
|
||||
setTimeout(() => {
|
||||
fetchUsecases(newarray)
|
||||
setFirstLoad(false)
|
||||
}, 100)
|
||||
fetchUsecases(newarray)
|
||||
setFirstLoad(false)
|
||||
}, 100)
|
||||
|
||||
} else {
|
||||
if (isLoggedIn) {
|
||||
@@ -1223,8 +1265,9 @@ const Workflows = (props) => {
|
||||
setView(tmpView);
|
||||
}
|
||||
|
||||
getApps()
|
||||
getAvailableWorkflows();
|
||||
getFramework()
|
||||
getFramework()
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -1280,6 +1323,7 @@ const Workflows = (props) => {
|
||||
color: "white",
|
||||
margin: "10px",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
position: "relative",
|
||||
};
|
||||
|
||||
const workflowActionStyle = {
|
||||
@@ -1708,21 +1752,22 @@ const Workflows = (props) => {
|
||||
<MenuItem
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
setModalOpen(true);
|
||||
setEditingWorkflow(JSON.parse(JSON.stringify(data)));
|
||||
setNewWorkflowName(data.name);
|
||||
setNewWorkflowDescription(data.description);
|
||||
setDefaultReturnValue(data.default_return_value);
|
||||
if (data.tags !== undefined && data.tags !== null) {
|
||||
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)));
|
||||
}
|
||||
event.stopPropagation()
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
setIsEditing(true)
|
||||
setModalOpen(true);
|
||||
setEditingWorkflow(JSON.parse(JSON.stringify(data)));
|
||||
setNewWorkflowName(data.name);
|
||||
setNewWorkflowDescription(data.description);
|
||||
setDefaultReturnValue(data.default_return_value);
|
||||
if (data.tags !== undefined && data.tags !== null) {
|
||||
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)));
|
||||
}
|
||||
|
||||
if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) {
|
||||
setSelectedUsecases(data.usecase_ids)
|
||||
}
|
||||
})
|
||||
if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) {
|
||||
setSelectedUsecases(data.usecase_ids)
|
||||
}
|
||||
})
|
||||
}}
|
||||
key={"change"}
|
||||
>
|
||||
@@ -2654,6 +2699,7 @@ const Workflows = (props) => {
|
||||
renderCell: (params) => {},
|
||||
},
|
||||
];
|
||||
|
||||
let rows = [];
|
||||
rows = filteredWorkflows.map((data, index) => {
|
||||
let obj = {
|
||||
@@ -2671,17 +2717,55 @@ const Workflows = (props) => {
|
||||
className={classes.datagrid}
|
||||
rows={rows}
|
||||
columns={columns}
|
||||
pageSize={20}
|
||||
pageSize={25}
|
||||
checkboxSelection
|
||||
autoHeight
|
||||
density="standard"
|
||||
onSelectionModelChange={(newSelection) => {
|
||||
//setSelectedWorkflows(newSelection.selectionModel);
|
||||
console.log(newSelection)
|
||||
|
||||
setSelectedWorkflowIndexes(newSelection)
|
||||
}}
|
||||
selectionModel={selectedWorkflowIndexes}
|
||||
components={{
|
||||
Toolbar: GridToolbar,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <div style={gridContainer}>{workflowData}</div>;
|
||||
return (
|
||||
<div style={gridContainer}>
|
||||
<Tooltip title={`New Workflow`} placement="bottom">
|
||||
<IconButton
|
||||
style={{position: "absolute", top: 10, right: 50, zIndex: 1000}}
|
||||
onClick={() => {
|
||||
setModalOpen(true)
|
||||
setIsEditing(false)
|
||||
}}
|
||||
>
|
||||
<AddCircleIcon style={{}} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{filteredWorkflows.length === 0 ? null :
|
||||
<IconButton
|
||||
style={{position: "absolute", top: 10, right: 10, zIndex: 1000}}
|
||||
disabled={selectedWorkflowIndexes.length === 0}
|
||||
onClick={() => {
|
||||
setDeleteModalOpen(true)
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Delete ${selectedWorkflowIndexes.length} workflows`}
|
||||
placement="top"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
}
|
||||
{workflowData}
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
var total_count = 0
|
||||
@@ -3152,70 +3236,20 @@ const Workflows = (props) => {
|
||||
|
||||
const WorkflowView = () => {
|
||||
if (workflows.length === 0) {
|
||||
// Not going there yet
|
||||
//if ((userdata.tutorials !== undefined && userdata.tutorials !== null && !userdata.tutorials.includes("getting-started")) || userdata.tutorials === null) {
|
||||
// return <Navigate to="/getting-started" replace />;
|
||||
//}
|
||||
//return (
|
||||
// <div style={emptyWorkflowStyle}>
|
||||
// <Paper style={boxStyle}>
|
||||
// <div>
|
||||
// <h2>Welcome to Shuffle</h2>
|
||||
// </div>
|
||||
// <div>
|
||||
// <p>
|
||||
// <b>Shuffle</b> is a flexible, easy to use, automation platform
|
||||
// allowing users to integrate their services and devices freely.
|
||||
// It's made to significantly reduce the amount of manual labor,
|
||||
// and is focused on security applications.{" "}
|
||||
// <a
|
||||
// href="/docs/about"
|
||||
// style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
// >
|
||||
// Click here to learn more.
|
||||
// </a>
|
||||
// </p>
|
||||
// </div>
|
||||
// <div>
|
||||
// If you want to jump straight into it, click here to create your
|
||||
// first workflow:
|
||||
// </div>
|
||||
// <div style={{ display: "flex" }}>
|
||||
// <Button
|
||||
// id="second-step"
|
||||
// color="primary"
|
||||
// style={{ marginTop: "20px" }}
|
||||
// variant="outlined"
|
||||
// onClick={() => setModalOpen(true)}
|
||||
// >
|
||||
// New workflow
|
||||
// </Button>
|
||||
// <span style={{ paddingTop: 20, display: "flex" }}>
|
||||
// <Typography
|
||||
// style={{ marginTop: 5, marginLeft: 30, marginRight: 15 }}
|
||||
// >
|
||||
// ..OR
|
||||
// </Typography>
|
||||
// {workflowButtons}
|
||||
// </span>
|
||||
// </div>
|
||||
// </Paper>
|
||||
// </div>
|
||||
//)
|
||||
}
|
||||
|
||||
var workflowDelay = -150
|
||||
var appDelay = -75
|
||||
var workflowDelay = -150
|
||||
var appDelay = -75
|
||||
|
||||
const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
return (
|
||||
<div style={viewStyle}>
|
||||
<div style={workflowViewStyle}>
|
||||
<div style={{ display: "flex", marginTop: 25, }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Typography variant="h1" style={{fontSize: 30}}>
|
||||
Workflows
|
||||
</Typography>
|
||||
<Typography variant="h1" style={{fontSize: 30}}>
|
||||
Workflows
|
||||
</Typography>
|
||||
</div>
|
||||
{/*
|
||||
<div style={{ flex: 1 }}>
|
||||
@@ -3243,6 +3277,7 @@ const Workflows = (props) => {
|
||||
minWidth: 275,
|
||||
},
|
||||
}}
|
||||
rows={1}
|
||||
placeholder="Filter Workflows"
|
||||
color="primary"
|
||||
fullWidth
|
||||
@@ -3268,53 +3303,6 @@ const Workflows = (props) => {
|
||||
{workflowButtons}
|
||||
</div>
|
||||
</div>
|
||||
{/*
|
||||
<div style={flexContainerStyle}>
|
||||
<div style={{...flexBoxStyle, ...activeWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={mobileImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>ACTIVE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...availableWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bookImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>AVAILABE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...notificationStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bagImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>NOTIFICATIONS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
{/*
|
||||
chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => {
|
||||
console.log("VALUE: ", value)
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={key}
|
||||
style={chipStyle}
|
||||
|
||||
>
|
||||
{value}
|
||||
</Chip>
|
||||
)
|
||||
}}
|
||||
*/}
|
||||
|
||||
<div style={{width: "100%", minHeight: isMobile ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}>
|
||||
{!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
@@ -3431,8 +3419,8 @@ const Workflows = (props) => {
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
height: imgSize,
|
||||
width: imgSize,
|
||||
height: imgSize+4,
|
||||
width: imgSize+4,
|
||||
position: "absolute",
|
||||
top: -2,
|
||||
left: -2,
|
||||
@@ -3772,12 +3760,12 @@ const Workflows = (props) => {
|
||||
}
|
||||
|
||||
const gettingStartedDrawer =
|
||||
<Drawer
|
||||
anchor={"right"}
|
||||
open={drawerOpen}
|
||||
variant="persistent"
|
||||
keepMounted={true}
|
||||
PaperProps={{
|
||||
<Drawer
|
||||
anchor={"right"}
|
||||
open={drawerOpen}
|
||||
variant="persistent"
|
||||
keepMounted={true}
|
||||
PaperProps={{
|
||||
style: {
|
||||
resize: "both",
|
||||
overflow: "auto",
|
||||
@@ -3928,6 +3916,7 @@ const Workflows = (props) => {
|
||||
{modalOpen === true ?
|
||||
<EditWorkflow
|
||||
globalUrl={globalUrl}
|
||||
userdata={userdata}
|
||||
workflow={editingWorkflow}
|
||||
setWorkflow={setEditingWorkflow}
|
||||
modalOpen={modalOpen}
|
||||
@@ -3938,6 +3927,7 @@ const Workflows = (props) => {
|
||||
isEditing={isEditing}
|
||||
|
||||
workflows={workflows}
|
||||
apps={apps}
|
||||
setWorkflows={setWorkflows}
|
||||
/>
|
||||
: null}
|
||||
|
||||
@@ -55,7 +55,10 @@ import (
|
||||
|
||||
// Starts jobs in bulk, so this could be increased
|
||||
var sleepTime = 3
|
||||
var maxConcurrency = 15
|
||||
|
||||
// Making it work on low-end machines even during busy times :)
|
||||
// May cause some things to run slowly
|
||||
var maxConcurrency = 2
|
||||
|
||||
// Timeout if something rashes
|
||||
var workerTimeoutEnv = os.Getenv("SHUFFLE_ORBORUS_EXECUTION_TIMEOUT")
|
||||
@@ -664,7 +667,7 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
||||
|
||||
hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId))
|
||||
|
||||
if strings.ToLower(cleanupEnv) == "true" {
|
||||
if strings.ToLower(cleanupEnv) != "false" {
|
||||
hostConfig.AutoRemove = true
|
||||
}
|
||||
|
||||
@@ -1095,10 +1098,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Cleanup
|
||||
// var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP"))
|
||||
// SHUFFLE_CONTAINER_AUTO_CLEANUP=false
|
||||
if strings.ToLower(os.Getenv("SHUFFLE_CONTAINER_AUTO_CLEANUP")) == "true" {
|
||||
// Handle Cleanup - made it cleanup by default
|
||||
if strings.ToLower(os.Getenv("SHUFFLE_CONTAINER_AUTO_CLEANUP")) != "false" {
|
||||
cleanupEnv = "true"
|
||||
}
|
||||
|
||||
@@ -1265,7 +1266,7 @@ func main() {
|
||||
|
||||
// FIXME - add check for StatusCode
|
||||
if newresp.StatusCode != 200 {
|
||||
log.Printf("[ERROR] Backend configuration missing (%d): %s", newresp.StatusCode, string(body))
|
||||
log.Printf("[ERROR] Backend connection failed, or is missing (%d): %s", newresp.StatusCode, string(body))
|
||||
} else {
|
||||
if !hasStarted {
|
||||
log.Printf("[DEBUG] Starting iteration on environment %#v (default = Shuffle). Got statuscode %d from backend on first request", environment, newresp.StatusCode)
|
||||
@@ -1340,7 +1341,7 @@ func main() {
|
||||
}
|
||||
|
||||
if shuffle.ArrayContains(executionIds, execution.ExecutionId) {
|
||||
log.Printf("[INFO] Execution already handled: %s", execution.ExecutionId)
|
||||
log.Printf("[INFO] Execution already handled (rerun of old executions): %s", execution.ExecutionId)
|
||||
toBeRemoved.Data = append(toBeRemoved.Data, execution)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
NAME=shuffle-worker
|
||||
VERSION=1.2.1
|
||||
VERSION=1.2.2
|
||||
|
||||
echo "Running docker build with $NAME:$VERSION"
|
||||
#CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
|
||||
@@ -11,10 +11,10 @@ docker build . -t frikky/shuffle:$NAME -t frikky/shuffle:$NAME_$VERSION -t docke
|
||||
#docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION
|
||||
#docker tag frikky/shuffle:0.8.51 ghcr.io/frikky/shuffle-worker:0.8.5
|
||||
#docker tag frikky/shuffle:$NAME ghcr.io/frikky/shuffle-worker:0.8.52
|
||||
docker push frikky/shuffle:$NAME
|
||||
docker push ghcr.io/frikky/$NAME:$VERSION
|
||||
docker push ghcr.io/frikky/$NAME:nightly
|
||||
|
||||
docker push shuffle/shuffle:$NAME
|
||||
docker push ghcr.io/shuffle/$NAME:$VERSION
|
||||
docker push ghcr.io/shuffle/$NAME:nightly
|
||||
#docker push frikky/shuffle:$NAME
|
||||
#docker push ghcr.io/frikky/$NAME:$VERSION
|
||||
#docker push ghcr.io/frikky/$NAME:nightly
|
||||
#
|
||||
#docker push shuffle/shuffle:$NAME
|
||||
#docker push ghcr.io/shuffle/$NAME:$VERSION
|
||||
#docker push ghcr.io/shuffle/$NAME:nightly
|
||||
|
||||
@@ -11,7 +11,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.4.17
|
||||
github.com/shuffle/shuffle-shared v0.4.50
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -47,6 +47,8 @@ require (
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opensearch-project/opensearch-go v1.1.0 // indirect
|
||||
github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
|
||||
@@ -60,17 +60,34 @@ 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/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
|
||||
github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
|
||||
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-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
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/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=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/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-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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=
|
||||
@@ -86,6 +103,7 @@ 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/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM=
|
||||
github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ=
|
||||
@@ -144,6 +162,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
|
||||
@@ -155,6 +174,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
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-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
@@ -181,6 +201,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
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=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@@ -201,6 +223,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -214,6 +240,8 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shuffle/shuffle-shared v0.4.17 h1:56ll366bdmIJu/7GFqNC2XTjjl0SGBf430PSq+EB6Ro=
|
||||
github.com/shuffle/shuffle-shared v0.4.17/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ=
|
||||
github.com/shuffle/shuffle-shared v0.4.50 h1:fJLfhWIJ5mYap4JwHnD/B5aaLyIULwylFSl3FoWlajM=
|
||||
github.com/shuffle/shuffle-shared v0.4.50/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
|
||||
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -227,10 +255,12 @@ 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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/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=
|
||||
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=
|
||||
@@ -246,6 +276,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -314,6 +346,12 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
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.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
|
||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -336,6 +374,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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/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=
|
||||
@@ -367,15 +406,29 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/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-20220520151302-bc2c85ada10a/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 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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/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=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
Reference in New Issue
Block a user