#287: Basic import configuration done

This commit is contained in:
frikky
2021-04-05 17:14:39 +02:00
parent 07bbf19d33
commit 6690a8c68b
6 changed files with 496 additions and 331 deletions
+6 -49
View File
@@ -1506,49 +1506,6 @@ func getUserCount() (int, error) {
return count, nil
}
func handleGetSchedules(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
return
}
user, err := shuffle.HandleApiAuthentication(resp, request)
if err != nil {
log.Printf("Api authentication failed in set new workflowhandler: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
if user.Role != "admin" {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Admin required"}`))
return
}
ctx := context.Background()
schedules, err := getAllSchedules(ctx, user.ActiveOrg.Id)
if err != nil {
log.Printf("Failed getting schedules: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Couldn't get schedules"}`))
return
}
newjson, err := json.Marshal(schedules)
if err != nil {
log.Printf("Failed unmarshal: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking environments"}`)))
return
}
//log.Printf("Existing environments: %s", string(newjson))
resp.WriteHeader(200)
resp.Write(newjson)
}
func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
@@ -5379,7 +5336,7 @@ func runInit(ctx context.Context) {
// Gets schedules and starts them
log.Printf("Relaunching schedules")
schedules, err := getAllSchedules(ctx, "ALL")
schedules, err := shuffle.GetAllSchedules(ctx, "ALL")
if err != nil {
log.Printf("Failed getting schedules during service init: %s", err)
} else {
@@ -6214,18 +6171,18 @@ func initHandlers() {
/* Everything below here increases the counters*/
r.HandleFunc("/api/v1/workflows", shuffle.GetWorkflows).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows", shuffle.SetNewWorkflow).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}", shuffle.GetSpecificWorkflow).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}", shuffle.SaveWorkflow).Methods("PUT", "OPTIONS")
r.HandleFunc("/api/v1/workflows/schedules", handleGetSchedules).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/schedules", shuffle.HandleGetSchedules).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/executions", shuffle.GetWorkflowExecutions).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/executions/{key}/abort", shuffle.AbortExecution).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/schedule", scheduleWorkflow).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/workflows/download_remote", loadSpecificWorkflows).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/execute", executeWorkflow).Methods("GET", "POST", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/schedule/{schedule}", stopSchedule).Methods("DELETE", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/outlook", createOutlookSub).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/outlook/{triggerId}", handleDeleteOutlookSub).Methods("DELETE", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/executions", shuffle.GetWorkflowExecutions).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}/executions/{key}/abort", shuffle.AbortExecution).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}", deleteWorkflow).Methods("DELETE", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}", shuffle.SaveWorkflow).Methods("PUT", "OPTIONS")
r.HandleFunc("/api/v1/workflows/{key}", shuffle.GetSpecificWorkflow).Methods("GET", "OPTIONS")
// Triggers
r.HandleFunc("/api/v1/hooks/new", shuffle.HandleNewHook).Methods("POST", "OPTIONS")
+2 -183
View File
@@ -1079,7 +1079,7 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
}
if actionResult.Status == "WAITING" && actionResult.Action.AppName == "User Input" {
log.Printf("SHOULD WAIT A BIT AND RUN CLOUD STUFF WITH USER INPUT! WAITING!")
log.Printf("[INFO] SHOULD WAIT A BIT AND RUN CLOUD STUFF WITH USER INPUT! WAITING!")
var trigger shuffle.Trigger
err = json.Unmarshal([]byte(actionResult.Result), &trigger)
@@ -4919,187 +4919,6 @@ func getWorkflowExecutions(resp http.ResponseWriter, request *http.Request) {
resp.Write(newjson)
}
func getAllSchedules(ctx context.Context, orgId string) ([]ScheduleOld, error) {
var schedules []ScheduleOld
q := datastore.NewQuery("schedules").Filter("org = ", orgId)
if orgId == "ALL" {
q = datastore.NewQuery("schedules")
}
_, err := dbclient.GetAll(ctx, q, &schedules)
if err != nil {
return []ScheduleOld{}, err
}
return schedules, nil
}
//FIXME: Add cursor
//func shuffle.GetAllWorkflowApps(ctx context.Context, maxLen int) ([]shuffle.WorkflowApp, error) {
// var apps []WorkflowApp
// query := datastore.NewQuery("workflowapp").Order("-edited").Limit(10)
// //query := datastore.NewQuery("workflowapp").Order("-edited").Limit(40)
//
// cacheKey := fmt.Sprintf("workflowapps-sorted-%d", maxLen)
// if value, found := requestCache.Get(cacheKey); found {
// parsedValue := value.(*[]WorkflowApp)
// log.Printf("[INFO] Returning %d apps from cache", len(*parsedValue))
// return *parsedValue, nil
// }
//
// cursorStr := ""
//
// // NOT BEING UPDATED
// // FIXME: Update the app with the correct actions. HOW DOES THIS WORK??
// // Seems like only actions are wrong. Could get the app individually.
// // Guessing it's a memory issue.
// //Actions []WorkflowAppAction `json:"actions" yaml:"actions" required:true datastore:"actions,noindex"`
// //errors.New(nil)
// var err error
// for {
// it := dbclient.Run(ctx, query)
// //_, err = it.Next(&app)
// for {
// var app WorkflowApp
// _, err := it.Next(&app)
// if err != nil {
// break
// }
//
// if app.Name == "Shuffle Subflow" {
// continue
// }
//
// found := false
// //log.Printf("ACTIONS: %d - %s", len(app.Actions), app.Name)
// for _, innerapp := range apps {
// if innerapp.Name == app.Name {
// found = true
// break
// }
// }
//
// if !found {
// apps = append(apps, app)
// }
// }
//
// if err != iterator.Done {
// //log.Printf("[INFO] Failed fetching results: %v", err)
// //break
// }
//
// // Get the cursor for the next page of results.
// nextCursor, err := it.Cursor()
// if err != nil {
// log.Printf("Cursorerror: %s", err)
// break
// } else {
// //log.Printf("NEXTCURSOR: %s", nextCursor)
// nextStr := fmt.Sprintf("%s", nextCursor)
// if cursorStr == nextStr {
// break
// }
//
// cursorStr = nextStr
// query = query.Start(nextCursor)
// //cursorStr = nextCursor
// //break
// }
//
// if len(apps) > maxLen {
// break
// }
// }
//
// if len(apps) > 20 {
// log.Printf("[INFO] Setting %d apps in cache", len(apps))
// requestCache.Set(cacheKey, &apps, cache.DefaultExpiration)
// }
//
// //var allworkflowapps []WorkflowApp
// //_, err := dbclient.GetAll(ctx, query, &allworkflowapps)
// //if err != nil {
// // if strings.Contains(fmt.Sprintf("%s", err), "ResourceExhausted") {
// // //datastore.NewQuery("workflowapp").Limit(30).Order("-edited")
// // query = datastore.NewQuery("workflowapp").Order("-edited").Limit(25)
// // //q := q.Limit(25)
// // _, err := dbclient.GetAll(ctx, query, &allworkflowapps)
// // if err != nil {
// // return []WorkflowApp{}, err
// // }
// // } else {
// // return []WorkflowApp{}, err
// // }
// //}
//
// return apps, nil
//}
//func shuffle.GetAllWorkflowAppAuth(ctx context.Context, OrgId string) ([]shuffle.AppAuthenticationStorage, error) {
// var allworkflowapps []AppAuthenticationStorage
// q := datastore.NewQuery("workflowappauth").Filter("org_id = ", OrgId)
//
// _, err := dbclient.GetAll(ctx, q, &allworkflowapps)
// if err != nil {
// return []AppAuthenticationStorage{}, err
// }
//
// return allworkflowapps, nil
//}
//
//func getWorkflowAppAuthDatastore(ctx context.Context, id string) (*AppAuthenticationStorage, error) {
//
// key := datastore.NameKey("workflowappauth", id, nil)
// appAuth := &AppAuthenticationStorage{}
// // New struct, to not add body, author etc
// if err := dbclient.Get(ctx, key, appAuth); err != nil {
// return &AppAuthenticationStorage{}, err
// }
//
// return appAuth, nil
//}
//
//func shuffle.SetWorkflowAppAuthDatastore(ctx context.Context, workflowappauth AppAuthenticationStorage, id string) error {
// timeNow := int64(time.Now().Unix())
// if workflowappauth.Created == 0 {
// workflowappauth.Created = timeNow
// }
//
// workflowappauth.Edited = timeNow
//
// key := datastore.NameKey("workflowappauth", id, nil)
//
// // New struct, to not add body, author etc
// if _, err := dbclient.Put(ctx, key, &workflowappauth); err != nil {
// log.Printf("Error adding workflow app auth: %s", err)
// return err
// }
//
// return nil
//}
//
//// Hmm, so I guess this should use uuid :(
//// Consistency PLX
//func SetWorkflowAppDatastore(ctx context.Context, workflowapp WorkflowApp, id string) error {
// timeNow := int64(time.Now().Unix())
// if workflowapp.Created == 0 {
// workflowapp.Created = timeNow
// }
//
// workflowapp.Edited = timeNow
// key := datastore.NameKey("workflowapp", id, nil)
//
// // New struct, to not add body, author etc
// if _, err := dbclient.Put(ctx, key, &workflowapp); err != nil {
// log.Printf("Error adding workflow app: %s", err)
// return err
// }
//
// return nil
//}
// Starts a new webhook
func handleStopHook(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
@@ -5372,7 +5191,7 @@ func handleUserInput(trigger shuffle.Trigger, organizationId string, workflowId
return err
}
log.Printf("Should send email to %s during execution.", email)
log.Printf("[INFO] Should send email to %s during execution.", email)
}
if strings.Contains(triggerType, "sms") {
action := shuffle.CloudSyncJob{
+371 -24
View File
@@ -1,13 +1,24 @@
import React, {useState} from 'react';
import {Typography, } from '@material-ui/core';
import { InputAdornment, Tooltip, TextField, CircularProgress, ButtonGroup, Button, Avatar, ListItemAvatar, Typography, List, ListItem, ListItemText} from '@material-ui/core';
import {FavoriteBorder as FavoriteBorderIcon} from '@material-ui/icons';
// Handles workflow updates on first open to highlight the issues of the workflow
// Variables
// Action (exists, missing fields)
// Action auth
// Triggers
//
// Specifically used for UNSAVED workflows only?
const Workflow = (props) => {
const { workflow, appAuthentication, apps } = props
const { globalUrl, theme, workflow, appAuthentication, setSelectedAction, setAuthenticationModalOpen, setSelectedApp, apps, selectedAction,setConfigureWorkflowModalOpen, saveWorkflow, newWebhook, submitSchedule, referenceUrl, isCloud, } = props
const [requiredActions, setRequiredActions] = React.useState([])
const [requiredVariables, setRequiredVariables] = React.useState([])
const [requiredTriggers, setRequiredTriggers] = React.useState([])
const [previousAuth, setPreviousAuth] = React.useState(appAuthentication)
const [firstLoad, setFirstLoad] = React.useState("")
var finished = false
// Rofl
if (workflow === undefined || workflow === null) {
return null
}
@@ -20,63 +31,349 @@ const Workflow = (props) => {
return null
}
const getApp = (actionId, appId) => {
fetch(globalUrl+"/api/v1/apps/"+appId+"/config?openapi=false", {
headers: {
'Accept': 'application/json',
},
credentials: "include",
})
.then((response) => {
if (response.status === 200) {
//alert.success("Successfully GOT app "+appId)
} else {
alert.error("Failed getting app")
}
return response.json()
})
.then((responseJson) => {
console.log("ACTION: ", responseJson)
if (responseJson.actions !== undefined && responseJson.actions !== null) {
}
})
.catch(error => {
alert.error(error.toString())
});
}
if (firstLoad.length === 0 || firstLoad !== workflow.id) {
if (finished) {
setConfigureWorkflowModalOpen(false)
return null
}
setFirstLoad(workflow.id)
const newactions = []
for (var key in workflow.actions) {
const action = workflow.actions[key]
var newaction = {
"large_image": "",
"app_name": "",
"app_version": "",
"large_image": action.large_image,
"app_name": action.app_name,
"app_version": action.app_version,
"activation_done": false,
"must_activate": false,
"must_authenticate": false,
"auth_done": false,
"action_ids": [],
"action": action,
"app": {},
}
const action = workflow.actions[key]
console.log(action)
const app = apps.find(app => app.name === action.app_name && app.app_version === action.app_version)
const app = apps.find(app => app.name === action.app_name && (app.app_version === action.app_version || app.loop_versions.includes(action.app_version)))
if (app === undefined || app === null) {
console.log("COULDNT FIND APP - SEARCH BACKEND")
console.log("App not found!")
newaction.app_name = action.app_name
newaction.app_version = action.app_version
newaction.must_activate = true
} else {
newaction.app_name = app.name
newaction.app_version = app.app_version
console.log("APP: ", app)
if (action.authentication_id === "" && app.authentication.required === true) {
console.log("Requires auth!")
newaction.must_authenticate = true
newaction.action_ids.push(action.id)
}
//newaction.app_name = action.app_name
//newaction.app_name = action.app_version
newaction.app = app
}
if (action.errors !== undefined && action.errors !== null && action.errors.length > 0) {
console.log("Has errors!")
console.log("Node has errors!: ", action.errors)
}
console.log("NEWACTION: ", newaction)
if (newaction.must_authenticate || newaction.must_activate) {
if (newaction.must_authenticate) {
var authenticationOptions = []
for (var key in appAuthentication) {
const auth = appAuthentication[key]
if (auth.app.name === app.name && auth.active) {
console.log("Found auth: ", auth)
authenticationOptions.push(auth)
newaction.authenticationId = auth.id
break
}
}
console.log("APPAUTH: ", app.authentication, action)
if (newaction.authenticationId === null || newaction.authenticationId === undefined || newaction.authenticationId.length === "") {
console.log("FAILED to authentication node!")
newactions.push(newaction)
} else {
console.log("Skipping node as it's already authenticated.")
newaction.authentication = authenticationOptions
workflow.actions[key] = newaction
}
} else if (newaction.must_activate) {
if (newactions.find(tmpaction => tmpaction.app_id === newaction.app_id && tmpaction.app_name === newaction.app_name) !== undefined) {
console.log("Action already found.")
} else {
newactions.push(newaction)
}
}
}
for (var key in workflow.workflow_variables) {
const variable = workflow.workflow_variables[key]
if (variable.value === undefined || variable.value === undefined || variable.value.length < 2) {
variable.value = ""
variable.index = key
requiredVariables.push(variable)
}
}
for (var key in workflow.triggers) {
var trigger = workflow.triggers[key]
trigger.index = key
if (trigger.status === "running") {
continue
}
if (trigger.trigger_type === "SUBFLOW" || trigger.trigger_type === "USERINPUT") {
continue
}
requiredTriggers.push(trigger)
}
if (requiredTriggers.length === 0 && requiredVariables.length === 0 && newactions.length === 0) {
setConfigureWorkflowModalOpen(false)
}
console.log("VARIABLES: ", requiredVariables)
console.log("ACTIONS: ", newactions)
setRequiredTriggers(requiredTriggers)
setRequiredVariables(requiredVariables)
setRequiredActions(newactions)
}
console.log("AUTH: ", appAuthentication)
if (appAuthentication.length !== previousAuth.length) {
console.log("APP AUTH CHANGED!")
var newactions = []
for (var actionkey in requiredActions) {
var newaction = requiredActions[actionkey]
const app = newaction.app
for (var key in appAuthentication) {
const auth = appAuthentication[key]
if (auth.app.name === app.name && auth.active) {
console.log("FOUND AUTH FOR: ", auth.app.name)
newaction.auth_done = true
break
}
}
newactions.push(newaction)
}
setRequiredActions(newactions)
setPreviousAuth(appAuthentication)
// Set auth done to true
//"auth_done": false
}
const TriggerSection = (props) => {
const {trigger} = props
console.log(trigger)
return (
<div>
<ListItem>
<ListItemAvatar>
<Avatar variant="rounded">
<img alt={trigger.label} src={trigger.large_image} style={{width: 50}} />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={trigger.name}
secondary={trigger.description}
style={{}}
/>
{trigger.trigger_type === "WEBHOOK" && trigger.status !== "running" ?
<Button disabled={trigger.status === "running"} color="primary" variant="contained" onClick={() => {
workflow.triggers[trigger.index].status = "running"
if (workflow.triggers[trigger.index].parameters === null) {
workflow.triggers[trigger.index].parameters = [
{"name": "url", "value": referenceUrl+"webhook_"+trigger.id},
{"name": "tmp", "value": "webhook_"+trigger.id},
]
}
newWebhook(workflow.triggers[trigger.index])
saveWorkflow(workflow)
}}>
{trigger.status !== "running" ? "Start" : "Running"}
</Button>
:
trigger.trigger_type === "SCHEDULE" && trigger.status !== "running" ?
<Button disabled={trigger.status === "running"} color="primary" variant="contained" onClick={() => {
workflow.triggers[trigger.index].status = "running"
if (workflow.triggers[trigger.index].parameters === null) {
workflow.triggers[trigger.index].parameters = [
{"name": "cron", "value": isCloud ? "*/15 * * * *" : "120"},
{"name": "execution_argument", "value": '{"example": {"json": "is cool"}}'},
]
}
submitSchedule(workflow.triggers[trigger.index], trigger.index)
saveWorkflow(workflow)
}}>
{trigger.status !== "running" ? "Start" : "Running"}
</Button>
:
null}
{/*
<ListItemText
primary={
<TextField
style={{backgroundColor: theme.palette.inputColor, borderRadius: 5,}}
InputProps={{
style:{
color: "white",
minHeight: 50,
marginLeft: 5,
maxWidth: "95%",
fontSize: "1em",
},
endAdornment: (
<InputAdornment position="end">
</InputAdornment>
)
}}
fullWidth
color="primary"
type={"text"}
placeholder={`New value for ${trigger.name}`}
onChange={(event) => {
console.log("NEW VALUE ON INDEX", trigger.value)
}}
onBlur={(event) => {
//workflow.variables[variable.index] = event.target.value
}}
/>
}
style={{}}
/>
*/}
</ListItem>
</div>
)
}
const VariableSection = (props) => {
const {variable} = props
//<Typography variant="body2">Name: {variable.name} - {variable.value}. </Typography>
return (
<ListItem>
<ListItemAvatar>
<Avatar>
<FavoriteBorderIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={variable.name}
secondary={variable.description}
style={{}}
/>
<ListItemText
primary={
<TextField
style={{backgroundColor: theme.palette.inputColor, borderRadius: 5,}}
InputProps={{
style:{
color: "white",
minHeight: 50,
marginLeft: 5,
maxWidth: "95%",
fontSize: "1em",
},
endAdornment: (
<InputAdornment position="end">
</InputAdornment>
)
}}
fullWidth
color="primary"
type={"text"}
placeholder={`New value for ${variable.name}`}
onChange={(event) => {
console.log("NEW VALUE ON INDEX", variable.index, variable.value)
}}
onBlur={(event) => {
workflow.workflow_variables[variable.index].value = event.target.value
}}
/>
}
style={{}}
/>
</ListItem>
)
}
const AppSection = (props) => {
const {action} = props
return (
<ListItem>
<ListItemAvatar>
<Avatar variant="rounded">
<img alt={action.app_name} src={action.large_image} style={{width: 50}} />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={action.app_name}
secondary={action.app_version}
style={{minWidth: 175, maxWidth: 175, marginLeft: 10}}
/>
{action.must_authenticate ?
action.auth_done ?
<div>
<Typography variant="body2">Name: {action.app_name}:{action.app_version}. </Typography>
<Button color="primary" variant="outlined" onClick={() => {
}}>
Finished
</Button>
</div>
:
selectedAction.app_name === action.app_name ?
<CircularProgress />
:
<Button color="primary" variant="contained" onClick={() => {
setSelectedAction(action.action)
setSelectedApp(action.app)
setAuthenticationModalOpen(true)
}}>
Authenticate
</Button>
:
null}
{action.must_activate ?
<Button color="primary" variant="contained" onClick={() => {
console.log("SHOULD ACTIVATE: ", action)
}}>
Activate
</Button>
: null}
</ListItem>
)
}
@@ -84,12 +381,62 @@ const Workflow = (props) => {
return (
<div>
<Typography variant="h6">Workflow: {workflow.id}</Typography>
<Typography variant="h6">{workflow.name}</Typography>
<Typography variant="body1" color="textSecondary">
The following configuration makes the workflow ready immediately.
</Typography>
{requiredActions.length > 0 ?
<span>
<Typography variant="body1" style={{marginTop: 10}}>Actions</Typography>
<List>
{requiredActions.map((data, index) => {
return (
<AppSection key={index} action={data} />
)
})}
</List>
</span>
: null}
{requiredVariables.length > 0 ?
<span>
<Typography variant="body1" style={{marginTop: 10}}>Variables</Typography>
{requiredVariables.map((data, index) => {
return (
<VariableSection key={index} variable={data} />
)
})}
</span>
: null}
{requiredTriggers.length > 0 ?
<span>
<Typography variant="body1" style={{marginTop: 10}}>Triggers</Typography>
{requiredTriggers.map((data, index) => {
return (
<TriggerSection key={index} trigger={data} />
)
})}
</span>
: null }
<div style={{textAlign: "center", display: "flex", marginTop: 20, }}>
<ButtonGroup style={{margin: "auto",}}>
<Button color="primary" variant={"outlined"} style={{
}} onClick={() => {
setConfigureWorkflowModalOpen(false)
}}>
Skip
</Button>
<Button color="primary" variant={"contained"} style={{
}} onClick={() => {
saveWorkflow(workflow)
window.location.reload()
}}>
Finish setup
</Button>
</ButtonGroup>
</div>
</div>
)
}
+30 -28
View File
@@ -459,9 +459,9 @@ const Admin = (props) => {
// FIXME: Set up features
Object.keys(responseJson.sync_features).map(function(key, index) {
//console.log(responseJson.sync_features[key])
})
//Object.keys(responseJson.sync_features).map(function(key, index) {
// //console.log(responseJson.sync_features[key])
//})
//setOrgName(responseJson.name)
//setOrgDescription(responseJson.description)
@@ -849,7 +849,6 @@ const Admin = (props) => {
return response.json()
})
.then((responseJson) => {
console.log(responseJson)
setSchedules(responseJson)
})
.catch(error => {
@@ -988,34 +987,45 @@ const Admin = (props) => {
});
}
const views = {
0: "organization",
1: "users",
2: "app_auth",
3: "files",
4: "schedules",
5: "environments",
6: "categories",
}
const setConfig = (event, newValue) => {
console.log("Value: ", newValue)
setCurTab(parseInt(newValue))
if (newValue === 1) {
document.title = "Shuffle - admin - users"
getUsers()
} else if (newValue === 2) {
document.title = "Shuffle - admin - app authentication"
getAppAuthentication()
} else if (newValue === 3) {
document.title = "Shuffle - admin - files"
getFiles()
} else if (newValue === 4) {
document.title = "Shuffle - admin - schedules"
getSchedules()
} else if (newValue === 5) {
document.title = "Shuffle - admin - environments"
getEnvironments()
} else if (newValue === 6) {
document.title = "Shuffle - admin - orgs"
getOrgs()
} else {
document.title = "Shuffle - admin"
}
if (newValue === 6) {
console.log("Should get apps for categories.")
}
const views = {
0: "organization",
1: "users",
2: "app_auth",
3: "environments",
4: "schedules",
5: "files",
6: "categories",
}
//var theURL = window.location.pathname
//FIXME: Add url edits
@@ -1027,33 +1037,21 @@ const Admin = (props) => {
//window.location.pathame = newpath
setModalUser({})
setCurTab(newValue)
}
if (firstRequest) {
setFirstRequest(false)
document.title = "Shuffle - admin"
if (!isCloud) {
getUsers()
} else {
getSettings()
}
const views = {
"organization": 0,
"users": 1,
"app_auth": 2,
"environments": 3,
"schedules": 4,
"files": 5,
}
if (props.match.params.key !== undefined) {
const tmpitem = views[props.match.params.key]
if (tmpitem !== undefined) {
//setCurTab(tmpitem)
setConfig("", tmpitem)
}
//const tmpitem = views[props.match.params.key]
setConfig("", props.match.params.key)
}
}
@@ -2092,11 +2090,13 @@ const Admin = (props) => {
</IconButton>
:
<Tooltip title={"Go to workflow"} style={{}} aria-label={"Download"}>
<span>
<a style={{textDecoration: "none", color: "#f85a3e"}} href={`/workflows/${file.workflow_id}`} target="_blank">
<IconButton disabled={file.workflow_id === "global"}>
<OpenInNewIcon style={{color: file.workflow_id !== "global" ? "white" : "grey",}} />
</IconButton>
</a>
</span>
</Tooltip>
}
style={{minWidth: 100, maxWidth: 100, overflow: "hidden"}}
@@ -2116,11 +2116,13 @@ const Admin = (props) => {
<ListItemText
primary=
<Tooltip title={"Download file"} style={{}} aria-label={"Download"}>
<span>
<IconButton disabled={file.status !== "active"} onClick={() => {
downloadFile(file)
}}>
<CloudDownloadIcon style={{color: file.status === "active" ? "white" : "grey",}} />
</IconButton>
</span>
</Tooltip>
style={{minWidth: 75, maxWidth: 75, overflow: "hidden"}}
/>
+60 -20
View File
@@ -128,7 +128,7 @@ const AngularWorkflow = (props) => {
const [rightSideBarOpen, setRightSideBarOpen] = React.useState(false)
const [showSkippedActions, setShowSkippedActions] = React.useState(false)
const [lastExecution, setLastExecution] = React.useState("")
const [configureWorkflowModalOpen, setConfigureWorkflowModalOpen] = React.useState(false)
const [configureWorkflowModalOpen, setConfigureWorkflowModalOpen] = React.useState(true)
const [curpath, setCurpath] = useState(typeof window === 'undefined' || window.location === undefined ? "" : window.location.pathname)
// 0 = normal, 1 = just done, 2 = normal
@@ -707,8 +707,10 @@ const AngularWorkflow = (props) => {
newBranches.push(parsedElement)
} else {
if (type === "ACTION") {
// FIXME - check whether position is new to not fuck up params etc.
var curworkflowAction = useworkflow.actions.find(a => a.id === cyelements[key].data()["id"])
const cyelement = cyelements[key].data()
const elementid = cyelement.id === undefined || cyelement.id === null ? cyelement["_id"] : cyelement.id
var curworkflowAction = useworkflow.actions.find(a => a !== undefined && (a["id"] === elementid || a["_id"] === elementid))
if (curworkflowAction === undefined) {
curworkflowAction = cyelements[key].data()
}
@@ -1146,6 +1148,12 @@ const AngularWorkflow = (props) => {
setWorkflow(responseJson)
setWorkflowDone(true)
//console.log(responseJson)
// Add error checks
if (!responseJson.public && (!responseJson.previously_saved || (!responseJson.is_valid || (responseJson.errors !== undefined || responseJson.errors !== null || responseJson.errors !== responseJson.errors.length > 0)))) {
setConfigureWorkflowModalOpen(true)
}
})
.catch(error => {
alert.error(error.toString())
@@ -1289,11 +1297,6 @@ const AngularWorkflow = (props) => {
//console.log("BRANCHES: ", branch)
if (data.type === "ACTION") {
// FIXME - unselect
//console.log(cy.elements('[_id!="${data._id}"]`))
// Does it choose the wrong action?
var curaction = workflow.actions.find(a => a.id === data.id)
if (!curaction || curaction === undefined) {
//event.target.unselect()
@@ -1301,15 +1304,27 @@ const AngularWorkflow = (props) => {
return
}
console.log(apps)
//console.log(apps)
const curapp = apps.find(a => a.name === curaction.app_name && (a.app_version === curaction.app_version || a.loop_versions.includes(curaction.app_version)))
if (!curapp || curapp === undefined) {
alert.error(`App ${curaction.app_name}:${curaction.app_version} not found. Is it activated?`)
const tmpapp = {
name: curaction.app_name,
app_name: curaction.app_name,
app_version: curaction.app_version,
id: curaction.app_id,
actions: [curaction],
}
console.log(tmpapp)
console.log(curaction)
setSelectedApp(tmpapp)
setSelectedAction(curaction)
//return
} else {
console.log("AUTHENTICATION: ", curapp.authentication)
//console.log("AUTHENTICATION: ", curapp.authentication)
setRequiresAuthentication(curapp.authentication.required && curapp.authentication.parameters !== undefined && curapp.authentication.parameters !== null)
if (curapp.authentication.required) {
// Setup auth here :)
@@ -2147,6 +2162,7 @@ const AngularWorkflow = (props) => {
}
const deleteVariable = (variableName) => {
console.log("Delete:" ,variableName)
workflow.workflow_variables = workflow.workflow_variables.filter(data => data.name !== variableName)
setWorkflow(workflow)
}
@@ -4232,8 +4248,6 @@ const AngularWorkflow = (props) => {
return response.json()
})
.then((responseJson) => {
console.log(responseJson)
if (setApp && responseJson.actions !== undefined && responseJson.actions !== null) {
if (selectedApp.versions !== undefined && selectedApp.versions !== null) {
responseJson.versions = selectedApp.versions
@@ -4244,17 +4258,14 @@ const AngularWorkflow = (props) => {
}
var foundAction = responseJson.actions.find(action => action.name === selectedAction.name)
console.log("Old : ", selectedAction)
console.log("Found: ", foundAction)
if (foundAction !== null && foundAction !== undefined) {
for (var paramkey in foundAction.parameters) {
const param = foundAction.parameters[paramkey]
const foundParam = selectedAction.parameters.find(item => item.name === param.name)
if (foundParam === undefined) {
console.log("COULDNT find Param: ", param)
//console.log("COULDNT find Param: ", param)
} else {
console.log("FoundP: ", foundParam)
foundAction.parameters[paramkey] = foundParam
}
}
@@ -7878,9 +7889,15 @@ const AngularWorkflow = (props) => {
}
}
console.log("Action: ", selectedAction)
selectedAction.authentication_id = authenticationOption.id
selectedAction.selectedAuthentication = authenticationOption
if (selectedAction.authentication === undefined || selectedAction.authentication === null) {
selectedAction.authentication = [authenticationOption]
} else {
selectedAction.authentication.push(authenticationOption)
}
setSelectedAction(selectedAction)
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption))
@@ -7898,6 +7915,12 @@ const AngularWorkflow = (props) => {
setNewAppAuth(newAuthOption)
//appAuthentication.push(newAuthOption)
//setAppAuthentication(appAuthentication)
//
if (configureWorkflowModalOpen) {
setSelectedAction({})
}
setUpdate(authenticationOption.id)
/*
@@ -8024,18 +8047,23 @@ const AngularWorkflow = (props) => {
<Dialog
open={configureWorkflowModalOpen}
onClose={() => {
setConfigureWorkflowModalOpen(false)
//setConfigureWorkflowModalOpen(false)
}}
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
minWidth: 600,
padding: 15,
padding: 50,
},
}}
>
<ConfigureWorkflow workflow={workflow} appAuthentication={appAuthentication} apps={apps} />
<IconButton style={{zIndex: 5000, position: "absolute", top: 14, right: 14, color: "grey"}} onClick={() => {
setConfigureWorkflowModalOpen(false)
}}>
<CloseIcon />
</IconButton>
<ConfigureWorkflow theme={theme} globalUrl={globalUrl} workflow={workflow} setSelectedAction={setSelectedAction} setSelectedApp={setSelectedApp} setAuthenticationModalOpen={setAuthenticationModalOpen} appAuthentication={appAuthentication} selectedAction={selectedAction} apps={apps} setConfigureWorkflowModalOpen={setConfigureWorkflowModalOpen} saveWorkflow={saveWorkflow} newWebhook={newWebhook} submitSchedule={submitSchedule} referenceUrl={referenceUrl} isCloud={isCloud} />
</Dialog>
: null
@@ -8046,6 +8074,10 @@ const AngularWorkflow = (props) => {
open={authenticationModalOpen}
onClose={() => {
//setAuthenticationModalOpen(false)
//
if (configureWorkflowModalOpen) {
setSelectedAction({})
}
}}
PaperProps={{
style: {
@@ -8056,6 +8088,14 @@ const AngularWorkflow = (props) => {
},
}}
>
<IconButton style={{zIndex: 5000, position: "absolute", top: 14, right: 14, color: "grey"}} onClick={() => {
setAuthenticationModalOpen(false)
if (configureWorkflowModalOpen) {
setSelectedAction({})
}
}}>
<CloseIcon />
</IconButton>
<DialogTitle><div style={{color: "white"}}>Authentication for {selectedApp.name}</div></DialogTitle>
<AuthenticationData app={selectedApp} />
</Dialog> : null
+3 -3
View File
@@ -585,7 +585,7 @@ const Workflows = (props) => {
for (var subkey in data.actions[key].parameters) {
const param = data.actions[key].parameters[subkey]
if (param.name.includes("key") || param.name.includes("user") || param.name.includes("pass") || param.name.includes("api") || param.name.includes("auth") || param.name.includes("secret")) {
if (param.name.includes("key") || param.name.includes("user") || param.name.includes("pass") || param.name.includes("api") || param.name.includes("auth") || param.name.includes("secret") || param.name.includes("domain") || param.name.includes("url")) {
// FIXME: This may be a vuln if api-keys are generated that start with $
if (param.value.startsWith("$")) {
console.log("Skipping field, as it's referencing a variable")
@@ -645,8 +645,8 @@ const Workflows = (props) => {
let exportFileDefaultName = data.name+'.json';
data = sanitizeWorkflow(data)
//console.log("EXPORT: ", data)
//return
// Add correct ID's for triggers
// Add mag
let dataStr = JSON.stringify(data)
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);