diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py
index 51690770..a7e1e7bf 100755
--- a/backend/app_sdk/app_base.py
+++ b/backend/app_sdk/app_base.py
@@ -1212,7 +1212,7 @@ class AppBase:
return results
# Downloads all files from a namespace
- # Currently only working on local version of Shuffle
+ # Currently only working on local version of Shuffle (2023)
def get_file_category_ids(self, category):
org_id = self.full_execution["workflow"]["execution_org"]["id"]
@@ -1914,7 +1914,6 @@ class AppBase:
c_parentheses = parse_nested_param(parse_string, 0)[0]
match_string = re.escape(c_parentheses)
custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string)
- print("[DEBUG] In ELSE: %s" % custom_casting)
# check if a wrapper was found
if len(custom_casting) != 0:
inner_result = parse_type(c_parentheses, custom_casting[0])
@@ -2093,7 +2092,6 @@ class AppBase:
except IndexError:
newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:])
else:
- #print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem))
if isinstance(firstitem, str):
if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end":
firstitem = len(basejson)-1
diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod
index 98f8f418..ccfc643b 100644
--- a/backend/go-app/go.mod
+++ b/backend/go-app/go.mod
@@ -1,6 +1,6 @@
module shuffle-shared
-replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
+//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
go 1.19
@@ -18,7 +18,7 @@ require (
github.com/gorilla/mux v1.8.0
github.com/h2non/filetype v1.1.3
github.com/satori/go.uuid v1.2.0
- github.com/shuffle/shuffle-shared v0.5.65
+ github.com/shuffle/shuffle-shared v0.5.78
golang.org/x/crypto v0.16.0
google.golang.org/api v0.125.0
google.golang.org/grpc v1.55.0
@@ -52,7 +52,7 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
- github.com/frikky/schemaless v0.0.5 // indirect
+ github.com/frikky/schemaless v0.0.6 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum
index 3bdc7d06..bdd5b7b7 100644
--- a/backend/go-app/go.sum
+++ b/backend/go-app/go.sum
@@ -176,8 +176,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
-github.com/frikky/schemaless v0.0.5 h1:ptQ0FpQpm/+e7HCYt7Wmc+vcMSelTYCRpKqtLVN2SOY=
-github.com/frikky/schemaless v0.0.5/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
+github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg=
+github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw=
@@ -455,6 +455,8 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
+github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYjG2KzncBE=
+github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
diff --git a/backend/go-app/main.go b/backend/go-app/main.go
index b2e901d5..0eca60ba 100755
--- a/backend/go-app/main.go
+++ b/backend/go-app/main.go
@@ -4953,6 +4953,7 @@ func initHandlers() {
// PS: For cloud, this has to use cloud storage.
// https://developer.box.com/reference/get-files-id-content/
// 1. Creating the "get file" option. Make it possible to run this in the frontend.
+ r.HandleFunc("/api/v1/files/download_remote", shuffle.HandleDownloadRemoteFiles).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/files/namespaces/{namespace}", shuffle.HandleGetFileNamespace).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/files/{fileId}/content", shuffle.HandleGetFileContent).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/files/create", shuffle.HandleCreateFile).Methods("POST", "OPTIONS")
diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go
index c1e71f2f..a9d72774 100755
--- a/backend/go-app/walkoff.go
+++ b/backend/go-app/walkoff.go
@@ -552,7 +552,9 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
ctx := context.Background()
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId)
if err != nil {
- log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err)
+ if len(actionResult.ExecutionId) > 0 {
+ log.Printf("[WARNING][%s] Failed getting execution (streamresult): %s", actionResult.ExecutionId, err)
+ }
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`)))
return
diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx
index 383553fd..fbdf7fa0 100644
--- a/frontend/src/components/AppFramework.jsx
+++ b/frontend/src/components/AppFramework.jsx
@@ -40,12 +40,12 @@ cytoscape.use(edgehandles);
export const findSpecificApp = (framework, inputcategory) => {
// Get the frameworkinfo for the org and fill in
if (framework === undefined || framework === null) {
- console.log("findSpecificApp: framework is null")
+ //console.log("findSpecificApp: framework is null")
return null
}
if (inputcategory === undefined || inputcategory === null) {
- console.log("findSpecificApp: category is null")
+ //console.log("findSpecificApp: category is null")
return null
}
@@ -118,8 +118,8 @@ export const findSpecificApp = (framework, inputcategory) => {
}
return {
- name: "EDR :default",
- large_image: parsedDatatypeImages["EDR & AV"],
+ name: "IAM :default",
+ large_image: parsedDatatypeImages["IAM"],
count: 0,
description: "",
id: "",
@@ -654,7 +654,7 @@ const AppFramework = (props) => {
const handleLoadNextSuggestion = (frameworkData) => {
- console.log("Should check for next apps to load from App suggestion model")
+ //console.log("Should check for next apps to load from App suggestion model")
//fetch(globalUrl + "/api/v1/workflows/usecases", {
//credentials: "include",
//cors: "no-cors",
@@ -720,7 +720,7 @@ const AppFramework = (props) => {
}
const showRecommendations = (changed, frameworkData) => {
- console.log("Inside recommendation loader")
+ //console.log("Inside recommendation loader")
setChangedApp(changed)
handleLoadNextSuggestion(frameworkData)
@@ -761,7 +761,7 @@ const AppFramework = (props) => {
//console.log("APptype, changed, framework: ", apptype.toLowerCase(), alternativeChanged.toLowerCase(), changed.toLowerCase(), frameworkData)
if (changed.toLowerCase() === apptype.toLowerCase() || changed.toLowerCase().includes(apptype.toLowerCase()) || alternativeChanged.toLowerCase() === apptype.toLowerCase() || alternativeChanged.toLowerCase().includes(apptype.toLowerCase())) {
potential = true
- console.log("Potential: !", apptype)
+ //console.log("Potential: !", apptype)
if (frameworkData[apptype] !== undefined && frameworkData[apptype].name !== undefined && frameworkData[apptype].name !== null && frameworkData[apptype].name.length > 0) {
usecase.items[itemtype].app = frameworkData[apptype]
@@ -819,10 +819,10 @@ const AppFramework = (props) => {
continue
}
} else {
- console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)")
+ //console.log("No usecase to try to match it to (usecase.usecase_references in UsecaseSearch)")
}
- console.log("Usecase: ", usecase)
+ //console.log("Usecase: ", usecase)
if (matches.length === usecase.items.length) {
usecase.color = "#c51152"
usecase.type = usecaseTypes[key].name
@@ -833,8 +833,8 @@ const AppFramework = (props) => {
}
// FIXME: Check if a usecase has already been handled
- console.log("")
- console.log("GOT USECASES: ", showusecases)
+ //console.log("")
+ //console.log("GOT USECASES: ", showusecases)
// FIXME: Just showing one usecase at a time for now
if (showusecases.length > 0) {
@@ -894,38 +894,37 @@ const AppFramework = (props) => {
parsedUsecase.process.push(edge.data)
}
- fetch(globalUrl + "/api/v1/workflows/usecases", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- body: JSON.stringify(parsedUsecase),
- credentials: "include",
- })
- .then((response) => {
- if (response.status !== 200) {
- console.log("Status not 200 for framework!");
- }
+ fetch(globalUrl + "/api/v1/workflows/usecases", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ body: JSON.stringify(parsedUsecase),
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for framework!");
+ }
- return response.json();
- })
- .then((responseJson) => {
- if (responseJson.success === false) {
- if (responseJson.reason !== undefined) {
- toast("Failed updating: " + responseJson.reason)
- } else {
- toast("Failed to update framework for your org.")
- }
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson.success === false) {
+ if (responseJson.reason !== undefined) {
+ toast("Failed updating: " + responseJson.reason)
} else {
- toast("Updated usecase.")
+ toast("Failed to update framework for your org.")
}
- })
- .catch((error) => {
- toast(error.toString());
- //setFrameworkLoaded(true)
- })
- }
+ } else {
+ toast("Updated usecase.")
+ }
+ })
+ .catch((error) => {
+ toast(error.toString());
+ })
+ }
const activateApp = (appid) => {
fetch(globalUrl+"/api/v1/apps/"+appid+"/activate", {
@@ -957,45 +956,54 @@ const AppFramework = (props) => {
}
const setFrameworkItem = (data) => {
- console.log("Setting framework item: ", data, isCloud)
if (!isCloud) {
activateApp(data.id)
}
- fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- body: JSON.stringify(data),
- credentials: "include",
- })
- .then((response) => {
- if (response.status !== 200) {
- console.log("Status not 200 for framework!");
- }
+ fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ body: JSON.stringify(data),
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for framework!");
+ }
- return response.json();
- })
- .then((responseJson) => {
- if (responseJson.success === false) {
- if (responseJson.reason !== undefined) {
- toast("Failed updating: " + responseJson.reason)
- } else {
- toast("Failed to update framework for your org.")
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson.success === false) {
+ if (responseJson.reason !== undefined) {
+ toast("Failed getting appframework: " + responseJson.reason)
+ } else {
+ toast("Failed to update framework for your org.")
- }
- }
+ }
+ } else {
+ if (data.id === "remove") {
+ frameworkData[data.type] = {}
+ frameworkData[data.type.toLowerCase()] = {}
+ } else {
+ frameworkData[data.type] = data
+ frameworkData[data.type.toLowerCase()] = data
+ }
- //setFrameworkLoaded(true)
- //setFrameworkData(responseJson)
- })
- .catch((error) => {
- toast(error.toString());
- //setFrameworkLoaded(true)
- })
- }
+ setDiscoveryData({})
+ if (setFrameworkData !== undefined) {
+ setFrameworkData(frameworkData)
+ }
+ }
+ })
+ .catch((error) => {
+ toast(error.toString());
+ //setFrameworkLoaded(true)
+ })
+ }
useEffect(() => {
if (!window.location.pathname.includes("usecases")) {
@@ -1058,7 +1066,6 @@ const AppFramework = (props) => {
frameworkData[keys[key]] = submitValue
}
- console.log("Frameworkdata: ", frameworkData)
setFrameworkData(frameworkData)
if (discoveryData.large_image !== undefined && discoveryData.large_image !== null && discoveryData.large_image.includes("storage.googleapis.com")) {
@@ -1076,8 +1083,6 @@ const AppFramework = (props) => {
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
-
-
const imgSize = 50;
var parsedFrameworkData = frameworkData === undefined ? {} : frameworkData
@@ -1263,7 +1268,7 @@ const AppFramework = (props) => {
var parsedStyle2 = {
"ghost": "yes",
- "border-width": "7px",
+ "border-width": "7px",
}
/*
@@ -1399,7 +1404,6 @@ const AppFramework = (props) => {
const onNodeSelect = (event) => {
var data = event.target.data();
- console.log("Node: ", data)
if (data.id === "SHUFFLE") {
event.target.unselect()
return
@@ -1465,6 +1469,15 @@ const AppFramework = (props) => {
const foundMiddleImage = 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 : '/images/Shuffle_logo.png'
+ const siemcheck = parsedFrameworkData.SIEM.large_image === undefined || (parsedFrameworkData.SIEM.name !== undefined && parsedFrameworkData.SIEM.name !== null && parsedFrameworkData.SIEM.name.includes(":default"))
+ const iamcheck = parsedFrameworkData.IAM.large_image === undefined || (parsedFrameworkData.IAM.name !== undefined && parsedFrameworkData.IAM.name !== null && parsedFrameworkData.IAM.name.includes(":default"))
+ const casescheck = parsedFrameworkData.Cases.large_image === undefined || (parsedFrameworkData.Cases.name !== undefined && parsedFrameworkData.Cases.name !== null && parsedFrameworkData.Cases.name.includes(":default"))
+ const assetscheck = parsedFrameworkData.Assets.large_image === undefined || (parsedFrameworkData.Assets.name !== undefined && parsedFrameworkData.Assets.name !== null && parsedFrameworkData.Assets.name.includes(":default"))
+ const intelcheck = parsedFrameworkData.Intel.large_image === undefined || (parsedFrameworkData.Intel.name !== undefined && parsedFrameworkData.Intel.name !== null && parsedFrameworkData.Intel.name.includes(":default"))
+ const commscheck = parsedFrameworkData.Comms.large_image === undefined || (parsedFrameworkData.Comms.name !== undefined && parsedFrameworkData.Comms.name !== null && parsedFrameworkData.Comms.name.includes(":default"))
+ const edrcheck = parsedFrameworkData["EDR & AV"].large_image === undefined || (parsedFrameworkData["EDR & AV"].name !== undefined && parsedFrameworkData["EDR & AV"].name !== null && parsedFrameworkData["EDR & AV"].name.includes(":default"))
+ const networkcheck = parsedFrameworkData.Network.large_image === undefined || (parsedFrameworkData.Network.name !== undefined && parsedFrameworkData.Network.name !== null && parsedFrameworkData.Network.name.includes(":default"))
+
const fontSize = `${12*scale}px`
const defaultSize = `${85*scale}px`
@@ -1484,11 +1497,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Cases.name === undefined ? "" : parsedFrameworkData.Cases.name,
description: parsedFrameworkData.Cases.description === undefined ? "" : parsedFrameworkData.Cases.description,
app_id: parsedFrameworkData.Cases.id === undefined ? "" : parsedFrameworkData.Cases.id,
- text_margin_y: parsedFrameworkData.Cases.large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData.Cases.large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData.Cases.large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData.Cases.large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: casescheck ? textMarginDefault : textMarginImage,
+ margin_x: casescheck ? `${32*scale}px` : "0px",
+ margin_y: casescheck ? `${19*scale}px` : `0px`,
+ width: casescheck ? iconSize : defaultSize,
+ height: casescheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Cases.large_image === undefined ? parsedDatatypeImages["CASES"] : parsedFrameworkData.Cases.large_image,
label: securityFramework[0].text.toUpperCase(),
@@ -1501,6 +1514,7 @@ const AppFramework = (props) => {
}
},
{
+
group: "nodes",
data: {
font_size: fontSize,
@@ -1512,11 +1526,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.IAM.name === undefined ? "" : parsedFrameworkData.IAM.name,
description: parsedFrameworkData.IAM.description === undefined ? "" : parsedFrameworkData.IAM.description,
app_id: parsedFrameworkData.IAM.id === undefined ? "" : parsedFrameworkData.IAM.id,
- text_margin_y: parsedFrameworkData.IAM.large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData.IAM.large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData.IAM.large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData.IAM.large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: iamcheck ? textMarginDefault : textMarginImage,
+ margin_x: iamcheck ? `${32*scale}px` : "0px",
+ margin_y: iamcheck ? `${19*scale}px` : `0px`,
+ width: iamcheck ? iconSize : defaultSize,
+ height: iamcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.IAM.large_image === undefined ? parsedDatatypeImages["IAM"] : parsedFrameworkData.IAM.large_image,
label: securityFramework[3].text.toUpperCase(),
@@ -1540,11 +1554,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Assets.name === undefined ? "" : parsedFrameworkData.Assets.name,
description: parsedFrameworkData.Assets.description === undefined ? "" : parsedFrameworkData.Assets.description,
app_id: parsedFrameworkData.Assets.id === undefined ? "" : parsedFrameworkData.Assets.id,
- text_margin_y: parsedFrameworkData.Assets.large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData.Assets.large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData.Assets.large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData.Assets.large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: assetscheck ? textMarginDefault : textMarginImage,
+ margin_x: assetscheck ? `${32*scale}px` : "0px",
+ margin_y: assetscheck ? `${19*scale}px` : `0px`,
+ width: assetscheck ? iconSize : defaultSize,
+ height: assetscheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Assets.large_image === undefined ? parsedDatatypeImages["ASSETS"] : parsedFrameworkData.Assets.large_image,
label: securityFramework[2].text.toUpperCase(),
@@ -1568,11 +1582,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Intel.name === undefined ? "" : parsedFrameworkData.Intel.name,
description: parsedFrameworkData.Intel.description === undefined ? "" : parsedFrameworkData.Intel.description,
app_id: parsedFrameworkData.Intel.id === undefined ? "" : parsedFrameworkData.Intel.id,
- text_margin_y: parsedFrameworkData.Intel.large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData.Intel.large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData.Intel.large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData.Intel.large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: intelcheck ? textMarginDefault : textMarginImage,
+ margin_x: intelcheck ? `${32*scale}px` : "0px",
+ margin_y: intelcheck ? `${19*scale}px` : `0px`,
+ width: intelcheck ? iconSize : defaultSize,
+ height: intelcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Intel.large_image === undefined ? parsedDatatypeImages["INTEL"] : parsedFrameworkData.Intel.large_image,
label: securityFramework[4].text.toUpperCase(),
@@ -1596,11 +1610,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Comms.name === undefined ? "" : parsedFrameworkData.Comms.name,
description: parsedFrameworkData.Comms.description === undefined ? "" : parsedFrameworkData.Comms.description,
app_id: parsedFrameworkData.Comms.id === undefined ? "" : parsedFrameworkData.Comms.id,
- text_margin_y: parsedFrameworkData.Comms.large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData.Comms.large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData.Comms.large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData.Comms.large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: commscheck ? textMarginDefault : textMarginImage,
+ margin_x: commscheck ? `${32*scale}px` : "0px",
+ margin_y: commscheck ? `${19*scale}px` : `0px`,
+ width: commscheck ? iconSize : defaultSize,
+ height: commscheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Comms.large_image === undefined ? parsedDatatypeImages["COMMS"] : parsedFrameworkData.Comms.large_image,
label: securityFramework[5].text.toUpperCase(),
@@ -1624,11 +1638,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData["EDR & AV"].name === undefined ? "" : parsedFrameworkData["EDR & AV"].name,
description: parsedFrameworkData["EDR & AV"].description === undefined ? "" : parsedFrameworkData["EDR & AV"].description,
app_id: parsedFrameworkData["EDR & AV"].id === undefined ? "" : parsedFrameworkData["EDR & AV"].id,
- text_margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData["EDR & AV"].large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData["EDR & AV"].large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: edrcheck ? textMarginDefault : textMarginImage,
+ margin_x: edrcheck ? `${32*scale}px` : "0px",
+ margin_y: edrcheck ? `${19*scale}px` : `0px`,
+ width: edrcheck ? iconSize : defaultSize,
+ height: edrcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData["EDR & AV"].large_image === undefined ? parsedDatatypeImages["EDR & AV"] : parsedFrameworkData["EDR & AV"].large_image,
label: securityFramework[7].text.toUpperCase(),
@@ -1652,11 +1666,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.Network.name === undefined ? "" : parsedFrameworkData.Network.name,
description: parsedFrameworkData.Network.description === undefined ? "" : parsedFrameworkData.Network.description,
app_id: parsedFrameworkData.Network.id === undefined ? "" : parsedFrameworkData.Network.id,
- text_margin_y: parsedFrameworkData.Network.large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData.Network.large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData.Network.large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData.Network.large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: networkcheck ? textMarginDefault : textMarginImage,
+ margin_x: networkcheck ? `${32*scale}px` : "0px",
+ margin_y: networkcheck ? `${19*scale}px` : `0px`,
+ width: networkcheck ? iconSize : defaultSize,
+ height: networkcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.Network.large_image === undefined ? parsedDatatypeImages["NETWORK"] : parsedFrameworkData.Network.large_image,
label: securityFramework[6].text.toUpperCase(),
id: securityFramework[6].text.toUpperCase(),
@@ -1679,11 +1693,11 @@ const AppFramework = (props) => {
name: parsedFrameworkData.SIEM.name === undefined ? "" : parsedFrameworkData.SIEM.name,
description: parsedFrameworkData.SIEM.description === undefined ? "" : parsedFrameworkData.SIEM.description,
app_id: parsedFrameworkData.SIEM.id === undefined ? "" : parsedFrameworkData.SIEM.id,
- text_margin_y: parsedFrameworkData.SIEM.large_image === undefined ? textMarginDefault : textMarginImage,
- margin_x: parsedFrameworkData.SIEM.large_image === undefined ? `${32*scale}px` : "0px",
- margin_y: parsedFrameworkData.SIEM.large_image === undefined ? `${19*scale}px` : `0px`,
- width: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize,
- height: parsedFrameworkData.SIEM.large_image === undefined ? iconSize : defaultSize,
+ text_margin_y: siemcheck ? textMarginDefault : textMarginImage,
+ margin_x: siemcheck ? `${32*scale}px` : "0px",
+ margin_y: siemcheck ? `${19*scale}px` : `0px`,
+ width: siemcheck ? iconSize : defaultSize,
+ height: siemcheck ? iconSize : defaultSize,
large_image: parsedFrameworkData.SIEM.large_image === undefined ? parsedDatatypeImages["SIEM"] : parsedFrameworkData.SIEM.large_image,
label: securityFramework[1].text.toUpperCase(),
id: securityFramework[1].text.toUpperCase(),
@@ -1988,6 +2002,7 @@ const AppFramework = (props) => {
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
return (
+ {/*
{
inputSearch={changedApp}
apps={apps}
/>
-
- {injectedApps.map((apps, appindex) => {
+
+ */}
+
+ {/*injectedApps.map((apps, appindex) => {
var categoryTop = 100
var categoryLeft = 100
@@ -2028,8 +2045,11 @@ const AppFramework = (props) => {
return null
}
+ const scale = 0.9
+ const offsetTop = -70
+ const offsetLeft = 0
return (
-
+
{apps.map((app, appIndex) => {
return (
{
}
if (setFrameworkData !== undefined) {
- console.log("Setting frameworkdata")
// Find discoveryData.id
var keys = []
for (const [key, value] of Object.entries(frameworkData)) {
@@ -2088,7 +2107,7 @@ const AppFramework = (props) => {
})}
)
- })}
+ })*/}
{showOptions === false ? null :
diff --git a/frontend/src/components/AppSearchButtons.jsx b/frontend/src/components/AppSearchButtons.jsx
index 553b7e41..262843ad 100644
--- a/frontend/src/components/AppSearchButtons.jsx
+++ b/frontend/src/components/AppSearchButtons.jsx
@@ -37,7 +37,7 @@ import {
} from '@mui/material';
const AppSearchButtons = (props) => {
- const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props
+ const { userdata, globalUrl, appFramework, moreButton, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, getAppFramework, } = props
const ref = useRef()
let navigate = useNavigate();
@@ -48,8 +48,6 @@ const AppSearchButtons = (props) => {
useEffect(() => {
if (newSelectedApp !== undefined && setMissing != undefined) {
- console.log("AppSearchButtons: setMissing is defined!")
-
const submitAppFramework = {
"description": newSelectedApp.description,
"id": newSelectedApp.objectID,
@@ -60,11 +58,17 @@ const AppSearchButtons = (props) => {
setFrameworkItem(submitAppFramework)
setMissing(newSelectedApp)
+
+ if (getAppFramework !== undefined) {
+ setTimeout(() => {
+ getAppFramework()
+ }, 1000)
+ }
}
}, [newSelectedApp])
if (appType === undefined) {
- console.log("Apptype is required in AppSearchButtons")
+ //console.log("Apptype is required in AppSearchButtons")
return null;
}
diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx
index 81e9fd3d..e3938c65 100755
--- a/frontend/src/components/ConfigureWorkflow.jsx
+++ b/frontend/src/components/ConfigureWorkflow.jsx
@@ -77,10 +77,7 @@ const ConfigureWorkflow = (props) => {
const [checkStarted, setCheckStarted] = React.useState(false);
useEffect(() => {
- console.log("Required actions: ", requiredActions)
if (requiredActions.length === 0) {
- console.log("No more actions? Set parent to done?")
-
if (setConfigurationFinished !== undefined) {
setConfigurationFinished(true)
}
@@ -705,9 +702,9 @@ const ConfigureWorkflow = (props) => {
})
.then((responseJson) => {
if (!responseJson.success) {
- toast("Failed to set app auth: " + responseJson.reason);
+ toast("Failed to set app authentication: " + responseJson.reason);
} else {
- toast("App auth set for app " + app.name.replace("_", " "));
+ toast("App authentication set for app " + app.name.replace("_", " "));
setFinalized(true)
setOpened(false)
}
@@ -731,6 +728,7 @@ const ConfigureWorkflow = (props) => {
parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " ");
+ console.log("AUTH Action: ", action)
return (
{
{
setOpened(!opened);
+
+ // Scroll to it
+ const element = document.getElementById("app-config");
+ if (element) {
+ // Scroll down 100px
+ setTimeout(() => {
+ element.scrollIntoView({
+ behavior: "smooth",
+ top: 100,
+ })
+
+ //element.scrollIntoView({
+ // behavior: "smooth",
+ // block: "center",
+ // inline: "center"
+ //});
+ }, 250)
+ }
+
}}
>
@@ -1318,7 +1335,7 @@ const ConfigureWorkflow = (props) => {
: null}
-
+
{requiredActions.map((data, index) => {
// AppWrapper = Default in a workflow, only shows with steps
diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx
index 05e57184..081b6f92 100644
--- a/frontend/src/components/EditWorkflow.jsx
+++ b/frontend/src/components/EditWorkflow.jsx
@@ -146,8 +146,6 @@ const EditWorkflow = (props) => {
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
@@ -485,7 +483,7 @@ const EditWorkflow = (props) => {
-
+
{
diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx
index 670cba97..73dbcfb7 100644
--- a/frontend/src/components/Files.jsx
+++ b/frontend/src/components/Files.jsx
@@ -16,6 +16,11 @@ import {
Divider,
Select,
MenuItem,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Typography,
} from "@mui/material";
import {
@@ -30,9 +35,8 @@ import {
Add as AddIcon,
} from "@mui/icons-material";
-//import { useAlert
import Dropzone from "../components/Dropzone.jsx";
-import CodeEditor from "../components/ShuffleCodeEditor.jsx";
+import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
import theme from "../theme.jsx";
const Files = (props) => {
@@ -45,6 +49,13 @@ const Files = (props) => {
const [fileContent, setFileContent] = React.useState("");
const [openEditor, setOpenEditor] = React.useState(false);
const [renderTextBox, setRenderTextBox] = React.useState(false);
+ const [loadFileModalOpen, setLoadFileModalOpen] = React.useState(false);
+
+ const [field1, setField1] = React.useState("");
+ const [field2, setField2] = React.useState("");
+ const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/standards")
+ const [downloadBranch, setDownloadBranch] = React.useState("main");
+ const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
//const alert = useAlert();
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"]
@@ -67,7 +78,7 @@ const Files = (props) => {
}
- const runUpdateText = (text) =>{
+ const runUpdateText = (text) =>{
fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, {
method: "PUT",
headers: {
@@ -76,17 +87,34 @@ const Files = (props) => {
},
body:text,
credentials: "include",
- }).then((response) => {
+ })
+ .then((response) => {
if (response.status !== 200) {
console.log("Can't update file");
}
return response.json();
- })
- //console.log(text);
+ })
+ .then((responseJson) => {
+ if (responseJson.success === true) {
+ toast("Successfully updated file");
+ }
+ })
+ .catch((error) => {
+ toast("Error updating file: " + error.toString());
+ })
}
- const getFiles = () => {
- fetch(globalUrl + "/api/v1/files", {
+ const getFiles = (namespace) => {
+ var parsedurl = `${globalUrl}/api/v1/files`
+ if (namespace === undefined || namespace === "default") {
+
+ } else if (namespace !== undefined && namespace !== null && namespace !== "") {
+ parsedurl = `${globalUrl}/api/v1/files/namespaces/${namespace}?ids=true`
+ } else if (selectedNamespace !== undefined && selectedNamespace !== null && selectedNamespace !== "default" && selectedNamespace !== "") {
+ parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedNamespace}?ids=true`
+ }
+
+ fetch(parsedurl, {
method: "GET",
headers: {
"Content-Type": "application/json",
@@ -104,12 +132,23 @@ const Files = (props) => {
})
.then((responseJson) => {
if (responseJson.files !== undefined && responseJson.files !== null) {
- setFiles(responseJson.files);
- } else {
+ setFiles(responseJson.files);
+ } else if (responseJson.list !== undefined && responseJson.list !== null) {
+ // Set the "namespace" field in all items
+ if (namespace !== undefined && namespace !== null) {
+ responseJson.list.forEach((item) => {
+ item.namespace = namespace
+ item.filename = item.name
+ item.workflow_id = "global"
+ })
+ }
+
+ setFiles(responseJson.list);
+ } else {
setFiles([]);
}
- if (responseJson.namespaces !== undefined && responseJson.namespaces !== null) {
+ if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileNamespaces.length === 0 || responseJson.namespaces.length > fileNamespaces.length)) {
setFileNamespaces(responseJson.namespaces);
}
})
@@ -119,9 +158,214 @@ const Files = (props) => {
};
useEffect(() => {
- getFiles();
+ getFiles(selectedNamespace)
}, []);
+ const importStandardsFromUrl = (url, folder) => {
+ if (url === undefined || url === null || url.length < 5) {
+ toast("Please enter a valid URL");
+ return;
+ }
+
+ if (folder === undefined || folder === null || folder.length < 2) {
+ toast("Please enter a valid folder name")
+ return
+ }
+
+ const parsedData = {
+ url: url,
+ path: folder,
+ field_3: downloadBranch || "master",
+ };
+
+ if (field1.length > 0) {
+ parsedData["field_1"] = field1;
+ }
+
+ if (field2.length > 0) {
+ parsedData["field_2"] = field2;
+ }
+
+ toast(`Getting files from url ${url}. This may take a while if the repository is large. Please wait...`);
+ fetch(globalUrl + "/api/v1/files/download_remote", {
+ method: "POST",
+ mode: "cors",
+ headers: {
+ Accept: "application/json",
+ },
+ body: JSON.stringify(parsedData),
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status === 200) {
+ toast("Successfully loaded files from " + downloadUrl);
+ setLoadFileModalOpen(false);
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (!responseJson.success) {
+ if (responseJson.reason !== undefined) {
+ toast("Failed loading: " + responseJson.reason);
+ } else {
+ toast("Failed loading");
+ }
+ }
+ })
+ .catch((error) => {
+ toast(error.toString());
+ });
+ }
+
+ const handleGithubValidation = () => {
+ importStandardsFromUrl(downloadUrl, downloadFolder);
+ }
+
+ const fileDownloadModal = loadFileModalOpen ?
+ {}}
+ PaperProps={{
+ style: {
+ backgroundColor: theme.palette.surfaceColor,
+ color: "white",
+ minWidth: "800px",
+ minHeight: "320px",
+ },
+ }}
+ >
+
+
+ Load Files from Github
+
+
+ Files will be loaded from the repository and branch you specify, with the focus on files in one folder at a time. This is NOT recursive.
+
+
+
+ Repository URL (supported: github, gitlab, bitbucket)
+ setDownloadUrl(e.target.value)}
+ placeholder="https://github.com/shuffle/standards"
+ fullWidth
+ />
+
+
+
+ Branch (default value is "main"):
+
+ setDownloadBranch(e.target.value)}
+ placeholder="master"
+ fullWidth
+ />
+
+
+
+ Folder (can use / for subfolders):
+
+ setDownloadFolder(e.target.value)}
+ placeholder="translation_standards"
+ fullWidth
+ />
+
+
+
+ Authentication (optional - private repos etc):
+
+
+ setField1(e.target.value)}
+ type="username"
+ placeholder="Username / APIkey (optional)"
+ fullWidth
+ />
+ setField2(e.target.value)}
+ type="password"
+ placeholder="Password (optional)"
+ fullWidth
+ />
+
+
+
+ setLoadFileModalOpen(false)}
+ color="primary"
+ >
+ Cancel
+
+ {
+ handleGithubValidation();
+ }}
+ color="primary"
+ >
+ Submit
+
+
+
+ : null
+
const deleteFile = (file) => {
fetch(globalUrl + "/api/v1/files/" + file.id, {
method: "DELETE",
@@ -140,7 +384,7 @@ const Files = (props) => {
})
.then((responseJson) => {
if (responseJson.success) {
- toast("Successfully deleted file " + file.name);
+ toast("Successfully deleted file")
} else if (
responseJson.reason !== undefined &&
responseJson.reason !== null
@@ -254,7 +498,7 @@ const Files = (props) => {
});
};
- const handleCreateFile = (filename, file) => {
+ const handleCreateFile = (filename, file) => {
var data = {
filename: filename,
org_id: selectedOrganization.id,
@@ -355,7 +599,7 @@ const Files = (props) => {
}
setTimeout(() => {
- getFiles();
+ getFiles()
}, 2500);
};
@@ -378,7 +622,21 @@ const Files = (props) => {
}}
onDrop={uploadFile}
>
-
+
+
+
+ setLoadFileModalOpen(true)}
+ >
+
+
+
+
+ {fileDownloadModal}
+
Files
@@ -393,6 +651,9 @@ const Files = (props) => {
+
+
+
{
+
+
{fileNamespaces !== undefined &&
fileNamespaces !== null &&
fileNamespaces.length > 1 ? (
@@ -442,8 +705,13 @@ const Files = (props) => {
}}
value={selectedNamespace}
onChange={(event) => {
- console.log("CHANGE NAMESPACE: ", event.target);
setSelectedNamespace(event.target.value);
+
+ if (event.target.value === "all" || event.target.value === "default") {
+ getFiles()
+ } else {
+ getFiles(event.target.value)
+ }
}}
>
{fileNamespaces.map((data, index) => {
@@ -453,7 +721,7 @@ const Files = (props) => {
value={data}
style={{ color: "white" }}
>
- {data}
+ {data.replaceAll("_", " ")}
);
})}
@@ -487,6 +755,8 @@ const Files = (props) => {
}
+
+
{renderTextBox &&
{
handleKeyDown(event);
@@ -504,7 +774,7 @@ const Files = (props) => {
autoFocus
/>}
-
{
/>
@@ -615,7 +885,7 @@ const Files = (props) => {
}}
/>
- ) : (
+ : (
{
};
- const returnHelperText = (name, value) => {
- if (name === undefined || value === undefined) {
- return ""
- }
-
- var helperText = ""
- //console.log("DATA: ", name, value)
- if (name.includes("url")) {
- if (value.includes("localhost") || value.includes("127.0.0.1")) {
- helperText = "Can't use localhost. Please change to an external IP or hostname."
- }
- }
-
- return helperText
+ const returnHelperText = (name, value) => {
+ if (name === undefined || value === undefined || name === null || value === null || name === "" || value === "") {
+ //console.log("RETURNING EMPTY")
+ return ""
}
+ var helperText = ""
+ //console.log("DATA: ", name, value)
+ if (name.includes("url")) {
+ if (value.includes("localhost") || value.includes("127.0.0.1")) {
+ helperText = "Can't use localhost. Please change to your external IP."
+ }
+ }
+
+ // Helpertext for openapi fields
+ //if (helperText === "" && name === "body" && selectedApp.generated && selectedApp.activated) {
+ // helperText =
+ //}
+
+ return helperText
+ }
+
//console.log("AUTH: ", authenticationType)
if (authenticationType !== undefined && authenticationType !== null && authenticationType.type === "oauth2") {
/*
@@ -1297,17 +1297,13 @@ const ParsedAction = (props) => {
}
var placeholder = "Value";
- if (
- data.example !== undefined &&
- data.example !== null &&
- data.example.length > 0
- ) {
+ if (data.example !== undefined && data.example !== null && data.example.length > 0) {
+
placeholder = data.example;
if (data.name === "url" && data.value !== undefined && data.value !== null && data.value.length === 0) {
data.value = data.example;
}
-
// In case of data.example
if (data.value === undefined || data.value === null) {
data.value = ""
@@ -1642,7 +1638,6 @@ const ParsedAction = (props) => {
),
}}
multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline}
- helperText={returnHelperText(data.name, data.value)}
onClick={() => {
/*
setExpansionModalOpen(false);
@@ -1680,18 +1675,7 @@ const ParsedAction = (props) => {
//changeActionParameterCodemirror(event, count, data)
changeActionParameter(event, count, data);
}}
- helperText={baseHelperText.length > 0 ? baseHelperText :
- selectedApp.generated &&
- selectedApp.activated &&
- data.name === "body" ? (
-
- {openApiHelperText}
-
- ) : data.name.startsWith("${") && data.name.endsWith("}") ?
- null : null
- }
+ helperText={returnHelperText(data.name, data.value)}
onBlur={(event) => {
baseHelperText = calculateHelpertext(event.target.value)
if (setLastSaved !== undefined) {
diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx
index 63c06515..bd6e5fa8 100644
--- a/frontend/src/components/Priorities.jsx
+++ b/frontend/src/components/Priorities.jsx
@@ -67,9 +67,15 @@ const Priorities = (props) => {
})
}
- const dismissNotification = (alert_id) => {
- // Don't really care about the logout
- fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, {
+ const dismissNotification = (alert_id, disabled) => {
+ var notificationurl = `${globalUrl}/api/v1/notifications/${alert_id}/markasread`
+ if (disabled === true) {
+ notificationurl += "?disabled=true"
+ } else if (disabled === false) {
+ notificationurl += "?disabled=false"
+ }
+
+ fetch(notificationurl , {
credentials: "include",
method: "GET",
headers: {
@@ -85,12 +91,50 @@ const Priorities = (props) => {
})
.then(function (responseJson) {
if (responseJson.success === true) {
- const newNotifications = notifications.filter(
- (data) => data.id !== alert_id
- );
- console.log("NEW NOTIFICATIONS: ", newNotifications);
+ // Mark current one as read
+ var newNotifications = notifications.map((notification) => {
+ if (notification.id === alert_id) {
+ notification.read = true
+ }
- if (setNotifications !== undefined) {
+ return notification
+ })
+
+
+ if (disabled === true) {
+ toast("Notification disabled, and will not be shown again.")
+
+ newNotifications = newNotifications.map((notification) => {
+ if (notification.id === alert_id) {
+ notification.ignored = true
+ }
+
+ return notification
+ })
+
+ console.log("NEW NOTIFICATIONS: ", newNotifications);
+ } else if (disabled === false) {
+ toast("Notification re-enabled successfully")
+
+ newNotifications = newNotifications.map((notification) => {
+ if (notification.id === alert_id) {
+ notification.ignored = false
+ }
+
+ return notification
+ })
+
+ } else {
+ toast("Notification dismissed successfully")
+ }
+
+ //const newNotifications = notifications.filter(
+ // (data) => data.id !== alert_id
+ //)
+
+ //console.log("NEW NOTIFICATIONS: ", newNotifications);
+
+ if (setNotifications !== undefined && newNotifications !== undefined) {
setNotifications(newNotifications)
}
} else {
@@ -174,6 +218,14 @@ const Priorities = (props) => {
style={{marginRight: 15, height: 25, }}
/>
: null}
+ {data.ignored === true ?
+
+ : null}
{data.read === false ?
{
Dismiss
) : null}
+
+ {
+ if (data.ignored === true) {
+ dismissNotification(data.id, false)
+ } else {
+ dismissNotification(data.id, true)
+ }
+ }}
+ >
+ {data.ignored === true ? "Re-enable" : "Disable"}
+
+
diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx
index 13c278d3..60e4bce2 100644
--- a/frontend/src/components/Priority.jsx
+++ b/frontend/src/components/Priority.jsx
@@ -32,7 +32,6 @@ const Priority = (props) => {
let newdescription = priority.description
const descsplit = priority.description.split("&")
if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) {
- console.log("descsplit: ", descsplit)
if (descsplit[1] === "") {
const item = findSpecificApp(appFramework, descsplit[0])
diff --git a/frontend/src/components/RenderCytoscape.jsx b/frontend/src/components/RenderCytoscape.jsx
index c112b448..7348fdb7 100644
--- a/frontend/src/components/RenderCytoscape.jsx
+++ b/frontend/src/components/RenderCytoscape.jsx
@@ -5,13 +5,13 @@ import cystyle from "../defaultCytoscapeStyle.jsx";
const surfaceColor = "#27292D";
const CytoscapeWrapper = (props) => {
- const { globalUrl, inworkflow } = props;
+ const { globalUrl, inworkflow, height, width } = props;
const [elements, setElements] = useState([]);
const [workflow, setWorkflow] = useState(inworkflow);
const [cy, setCy] = React.useState();
- const bodyWidth = 200;
- const bodyHeight = 150;
+ const bodyWidth = height === undefined ? 1000 : width
+ const bodyHeight = width === undefined ? 1000 : height
const setupGraph = () => {
const actions = workflow.actions.map((action) => {
@@ -76,29 +76,6 @@ const CytoscapeWrapper = (props) => {
};
// This is an attempt at prettier edges. The numbers are weird to work with.
- /*
- //http://manual.graphspace.org/projects/graphspace-python/en/latest/demos/edge-types.html
- const sourcenode = actions.find(node => node.data._id === branch.source_id)
- const destinationnode = actions.find(node => node.data._id === branch.destination_id)
- if (sourcenode !== undefined && destinationnode !== undefined && branch.source_id !== branch.destination_id) {
- //node.data._id = action["id"]
- console.log("SOURCE: ", sourcenode.position)
- console.log("DESTINATIONNODE: ", destinationnode.position)
-
- var opposite = true
- if (sourcenode.position.x > destinationnode.position.x) {
- opposite = false
- } else {
- opposite = true
- }
-
- edge.style = {
- 'control-point-distance': opposite ? ["25%", "-75%"] : ["-10%", "90%"],
- 'control-point-weight': ['0.3', '0.7'],
- }
- }
- */
-
return edge;
});
@@ -135,9 +112,10 @@ const CytoscapeWrapper = (props) => {
elements={elements}
minZoom={0.35}
maxZoom={2.0}
+ zoom={1.0}
style={{
- width: bodyWidth - 15,
- height: bodyHeight - 5,
+ width: bodyWidth,
+ height: bodyHeight,
backgroundColor: surfaceColor,
}}
stylesheet={cystyle}
diff --git a/frontend/src/components/RuntimeDebugger.jsx b/frontend/src/components/RuntimeDebugger.jsx
index 01bb345c..416b0d76 100644
--- a/frontend/src/components/RuntimeDebugger.jsx
+++ b/frontend/src/components/RuntimeDebugger.jsx
@@ -75,9 +75,15 @@ const RuntimeDebugger = (props) => {
setTotalCount(0)
+ var starttime = ""
+ var endtime = ""
var count = 0
- var starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString()
- var endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString()
+ try {
+ starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString()
+ endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString()
+ } catch (e) {
+ toast("Invalid date format", { type: "error" })
+ }
var maxworkflows = 5
@@ -88,6 +94,11 @@ const RuntimeDebugger = (props) => {
}
const workflowId = workflows[key].id
+
+ if (workflowId === undefined || workflowId === null || workflowId === "") {
+ continue
+ }
+
// Fetch the data for the workflow
var url = `${globalUrl}/api/v1/workflows/${workflowId}/executions/count`
if (starttime !== "") {
@@ -133,7 +144,6 @@ const RuntimeDebugger = (props) => {
}
setTimeout(() => {
- console.log("Setting total count: ", count)
setTotalCount(count)
}, maxworkflows*300)
}
@@ -578,7 +588,7 @@ const RuntimeDebugger = (props) => {
onClick={() => {
window.open(`${globalUrl}/api/v1/workflows/search/${params.row.id}`, "_blank")
}}
- disabled={!userdata.support}
+ disabled={userdata.region_url !== "https://shuffler.io"}
>
diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx
index 15aec446..3b6e02a2 100644
--- a/frontend/src/components/ShuffleCodeEditor.jsx
+++ b/frontend/src/components/ShuffleCodeEditor.jsx
@@ -57,7 +57,6 @@ import { padding, textAlign } from '@mui/system';
import data from '../frameworkStyle.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import { tags as t } from '@lezer/highlight';
-import { createTheme } from '@uiw/codemirror-themes';
@@ -84,37 +83,6 @@ const pythonFilters = [
{"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``},
]
-/*
-const shuffleTheme = createTheme({
- theme: 'dark',
- settings: {
- background: "rgba(40,40,40, 1)",
- foreground: '#75baff',
- caret: '#5d00ff',
- selection: '#036dd626',
- selectionMatch: '#036dd626',
- lineHighlight: '#8a91991a',
- gutterForeground: '#8a919966',
- },
- styles: [
- { tag: t.comment, color: '#787b8099' },
- { tag: t.variableName, color: '#0080ff' },
- { tag: [t.string, t.special(t.brace)], color: '#5c6166' },
- { tag: t.number, color: '#5c6166' },
- { tag: t.bool, color: '#5c6166' },
- { tag: t.null, color: '#5c6166' },
- { tag: t.keyword, color: '#5c6166' },
- { tag: t.operator, color: '#5c6166' },
- { tag: t.className, color: '#5c6166' },
- { tag: t.definition(t.typeName), color: '#5c6166' },
- { tag: t.typeName, color: '#5c6166' },
- { tag: t.angleBracket, color: '#5c6166' },
- { tag: t.tagName, color: '#5c6166' },
- { tag: t.attributeName, color: '#5c6166' },
- ],
-});
-*/
-
const CodeEditor = (props) => {
const {
globalUrl,
diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx
index 6d3413dc..6df11b9b 100644
--- a/frontend/src/components/WorkflowTemplatePopup.jsx
+++ b/frontend/src/components/WorkflowTemplatePopup.jsx
@@ -5,6 +5,7 @@ import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import AppSearchButtons from "../components/AppSearchButtons.jsx";
import { isMobile } from "react-device-detect";
+import RenderCytoscape from "../components/RenderCytoscape.jsx";
import {
Button,
Typography,
@@ -29,7 +30,7 @@ import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
const WorkflowTemplatePopup = (props) => {
- const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage } = props;
+ const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage, getAppFramework, } = props;
const [isActive, setIsActive] = useState(false);
const [isHovered, setIsHovered] = useState(false);
@@ -39,17 +40,41 @@ const WorkflowTemplatePopup = (props) => {
const [workflow, setWorkflow] = useState({});
const [showLoginButton, setShowLoginButton] = useState(false);
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
-
const [missingSource, setMissingSource] = React.useState(undefined)
const [missingDestination, setMissingDestination] = React.useState(undefined);
-
const [configurationFinished, setConfigurationFinished] = React.useState(false);
+ const [appSetupDone, setAppSetupDone] = React.useState(false)
- useEffect(() => {
- }, [missingSource, missingDestination])
-
- const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
+ const [requestSent, setRequestSent] = React.useState(false)
+
+ const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
let navigate = useNavigate();
+ useEffect(() => {
+ if (modalOpen !== true) {
+ return
+ }
+
+ if (!srcapp.includes(":default") && !dstapp.includes(":default")) {
+ if (appSetupDone === false && setAppSetupDone !== undefined) {
+ setAppSetupDone(true)
+ }
+
+ getGeneratedWorkflow()
+ }
+
+ if (missingSource !== undefined && missingDestination !== undefined) {
+ if (appSetupDone === false && setAppSetupDone !== undefined) {
+ setAppSetupDone(true)
+ }
+ }
+
+ if (getAppFramework !== undefined) {
+ setTimeout(() => {
+ getAppFramework()
+ }, 500)
+ }
+ }, [modalOpen, missingSource, missingDestination])
+
const imagestyleWrapper = {
height: 40,
@@ -272,6 +297,9 @@ const WorkflowTemplatePopup = (props) => {
// middle:[]
// name: "Email analysis"
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
+ if (requestSent === true) {
+ return
+ }
if (srcapp.includes(":default") || dstapp.includes(":default")) {
@@ -309,9 +337,8 @@ const WorkflowTemplatePopup = (props) => {
},
}
+ setRequestSent(true)
const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge`
- //const url = `https://shuffler.io/api/v1/workflows/merge`
-
fetch(url, {
method: "POST",
headers: {
@@ -324,6 +351,7 @@ const WorkflowTemplatePopup = (props) => {
.then((response) => {
if (response.status !== 200) {
//console.log("Status not 200 for framework!");
+ setRequestSent(false)
}
setWorkflowLoading(false)
@@ -341,6 +369,7 @@ const WorkflowTemplatePopup = (props) => {
if (responseJson.success === false) {
//console.log("Error in workflow template: ", responseJson.error);
+ setRequestSent(false)
const defaultMessage = "Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io for further assistance."
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") {
@@ -366,10 +395,21 @@ const WorkflowTemplatePopup = (props) => {
})
.catch((error) => {
console.log("err in framework: ", error.toString());
+ setRequestSent(false)
setWorkflowLoading(false)
})
}
+ if (modalOpen === true && !srcapp.includes(":default") && !dstapp.includes(":default")) {
+ if (appSetupDone === false && setAppSetupDone !== undefined) {
+ setAppSetupDone(true)
+ }
+
+ if (workflow.id === undefined && workflowLoading === false && errorMessage === "") {
+ getGeneratedWorkflow()
+ }
+ }
+
const isFinished = () => {
// Look for configuration fields being done in the current modal
// 1. Start by finding the modal
@@ -388,6 +428,13 @@ const WorkflowTemplatePopup = (props) => {
}
const ModalView = () => {
+ if (modalOpen === false) {
+ return null
+ }
+
+ const divHeight = 500
+ const divWidth = 500
+
return (
{
}
- {(missingSource !== undefined || missingDestination !== undefined) ?
+ {(appSetupDone === false && missingSource !== undefined || missingDestination !== undefined) ?
- {"Find relevevant Apps for this Usecase"}
+ {"Find relevant Apps for this Usecase"}
: null}
@@ -491,6 +538,8 @@ const WorkflowTemplatePopup = (props) => {
AppImage={missingSource.image}
setMissing={setMissingSource}
+
+ getAppFramework={getAppFramework}
/>
: null}
@@ -505,6 +554,8 @@ const WorkflowTemplatePopup = (props) => {
AppImage={missingDestination.image}
setMissing={setMissingDestination}
+
+ getAppFramework={getAppFramework}
/>
: null}
@@ -521,12 +572,29 @@ const WorkflowTemplatePopup = (props) => {
setConfigurationFinished={setConfigurationFinished}
/>
+
+ {/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id !== "" ?
+
+
+
+ : null*/}
+
{errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
-
-
+
+ {
+ // Open in new tab
+ window.open("/workflows/" + workflow.id, "_blank")
+ }}
+ >
{/* */}
-
- Workflow generated!
+
+ Workflow Successfully Generated!
@@ -648,6 +716,7 @@ const WorkflowTemplatePopup = (props) => {
: ""}
+
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx
index f653bd2c..984ebb30 100755
--- a/frontend/src/views/Admin.jsx
+++ b/frontend/src/views/Admin.jsx
@@ -5,8 +5,6 @@ import { makeStyles } from "@mui/styles";
import { useNavigate, Link } from "react-router-dom";
import countries from "../components/Countries.jsx";
-import CodeEditor from "../components/ShuffleCodeEditor.jsx";
-import getLocalCodeData from "../components/ShuffleCodeEditor.jsx";
import CacheView from "../components/CacheView.jsx";
import {
@@ -1867,16 +1865,24 @@ If you're interested, please let me know a time that works for you, or set up a
{selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ?
- Only the name of the auth can be modified for Oauth2. Please remake the authentication to change the fields like Client ID, Secret, Scopes etc.
+ Only the name and url can be modified for Oauth2/OpenID connect. Please remake the authentication if you want to change the other fields like Client ID, Secret, Scopes etc.
- :
- selectedAuthentication.fields.map((data, index) => {
+ : null }
+
+ {selectedAuthentication.fields.map((data, index) => {
var fieldname = data.key.replaceAll("_", " ")
if (fieldname.endsWith(" basic")) {
fieldname = fieldname.substring(0, fieldname.length - 6)
}
+ if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") {
+ if (selectedAuthentication.fields[index].key !== "url") {
+ return null
+ }
+ }
+
+
//console.log("DATA: ", data, selectedAuthentication)
return (
@@ -3415,24 +3421,26 @@ If you're interested, please let me know a time that works for you, or set up a
// using request id or trace id
-
@@ -4406,7 +4415,7 @@ If you're interested, please let me know a time that works for you, or set up a
/>
setDefaultEnvironment(environment)}
color="primary"
>
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx
index 3f397674..a54d2f16 100755
--- a/frontend/src/views/AngularWorkflow.jsx
+++ b/frontend/src/views/AngularWorkflow.jsx
@@ -121,7 +121,7 @@ import * as edgehandles from "cytoscape-edgehandles";
import CytoscapeComponent from "react-cytoscapejs";
import Draggable from "react-draggable";
import cytoscapestyle from "../defaultCytoscapeStyle.jsx";
-import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
+import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
@@ -13366,6 +13366,67 @@ const AngularWorkflow = (defaultprops) => {
{workflow.name}
+
+ {workflow.public === true || userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id ? null :
+
+ Warning : Change {
+ toast("Changing to correct organization. Please wait a few seconds.")
+
+ localStorage.setItem("globalUrl", "");
+ localStorage.setItem("getting_started_sidebar", "open");
+ fetch(`${globalUrl}/api/v1/orgs/${workflow.org_id}/change`, {
+ mode: "cors",
+ credentials: "include",
+ crossDomain: true,
+ method: "POST",
+ body: JSON.stringify({"org_id": workflow.org_id}),
+ withCredentials: true,
+ headers: {
+ "Content-Type": "application/json; charset=utf-8",
+ },
+ })
+ .then(function (response) {
+ if (response.status !== 200) {
+ console.log("Error in response");
+ }
+
+ return response.json();
+ })
+ .then(function (responseJson) {
+ console.log("In here?")
+ if (responseJson.success === true) {
+ if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) {
+ console.log("Region Change: ", responseJson.region_url);
+ localStorage.setItem("globalUrl", responseJson.region_url);
+ //globalUrl = responseJson.region_url
+ }
+
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+
+ toast("Successfully changed active organization - refreshing!");
+ } else {
+ if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
+ toast(responseJson.reason);
+ } else {
+ toast("Failed changing org. Try again or contact support@shuffler.io if this persists.");
+ }
+ }
+ })
+ .catch((error) => {
+ console.log("error changing: ", error);
+ //removeCookie("session_token", {path: "/"})
+ })
+
+
+
+ }}
+ >Active Organization to edit this Workflow.
+
+ }
{parentWorkflows.slice(0,5).map((wf, index) => {
@@ -13744,9 +13805,64 @@ const AngularWorkflow = (defaultprops) => {
: null
+
+
const RightsideBar = () => {
const [hovered, setHovered] = useState(false)
+ useEffect(() => {
+ const handleKeyDown = (event) => {
+ if ((event.metaKey || event.ctrlKey) && event.key === '/') {
+ event.preventDefault(); // Prevent default browser behavior (like opening search bar)
+ if (!workflow.public && !executionRequestStarted) {
+ executeWorkflow(executionText, workflow.start, lastSaved);
+ }
+ }
+ if ((event.ctrlKey || event.metaKey) && event.key === "'") {
+ // Check if Ctrl (Windows/Linux) or Command (Mac) key is pressed along with '/'
+ if (!workflow.public && !executionModalOpen) {
+ setExecutionModalOpen(true);
+ getWorkflowExecution(props.match.params.key, "");
+ } else if (!workflow.public && executionModalOpen) {
+ setExecutionModalOpen(false);
+ }
+ }
+
+ if ((event.ctrlKey || event.metaKey) && event.key === "]") {
+ console.log("Show workflow revisions key pressed")
+ if (!workflow.public) {
+ setShowWorkflowRevisions(true)
+ setSelectedRevision(workflow)
+ //setOriginalWorkflow(workflow)
+ }
+ }
+
+ if (( event.ctrlKey || event.metaKey ) && event.key === ";") {
+ if (!workflow.public && executionModalOpen) {
+ getWorkflowExecution(props.match.params.key, "");
+ }
+ }
+
+ if (( event.ctrlKey || event.metaKey ) && event.shiftKey) {
+ console.log("Shift key pressed")
+ if (!workflow.public && executionModalOpen) {
+ setExecutionRunning(false);
+ stop()
+ const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
+ const newitem = removeParam("execution_id", cursearch);
+ navigate(curpath + newitem)
+ setExecutionModalView(0);
+ }
+ }
+ };
+
+ document.addEventListener('keydown', handleKeyDown);
+
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [executeWorkflow, executionText, workflow, lastSaved, executionRequestStarted]);
+
return (
{
) : (
-
+
{
@@ -14069,7 +14185,7 @@ const AngularWorkflow = (defaultprops) => {
@@ -14080,8 +14196,8 @@ const AngularWorkflow = (defaultprops) => {
variant={"outlined"}
onClick={() => {
setShowWorkflowRevisions(true)
- setSelectedRevision(workflow)
- //setOriginalWorkflow(workflow)
+ setSelectedRevision(workflow)
+ //setOriginalWorkflow(workflow)
}}
>
@@ -14283,6 +14399,52 @@ const AngularWorkflow = (defaultprops) => {
//return null;
};
+ const unPublishWorkflow = (data) => {
+ if (!isCloud) {
+ toast("Function only supported on cloud")
+ return
+ }
+
+ if (data.public !== true) {
+ toast("Workflow is not public. Can't unpublish");
+ return
+ }
+
+ // This ALWAYS talks to Shuffle cloud
+ data = JSON.parse(JSON.stringify(data));
+ const url = `${globalUrl}/api/v1/workflows/${data.id}/unpublish`;
+ fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ body: JSON.stringify(data),
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for workflow publish :O!");
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson.reason !== undefined) {
+ toast("Unpublishing: "+responseJson.reason)
+ }
+
+ if (responseJson.success === true) {
+ workflow.public = false
+ setWorkflow(workflow)
+ }
+ })
+ .catch((error) => {
+ toast("Failed publishing: is the workflow valid? Remember to save the workflow first.")
+ console.log(error.toString())
+ })
+ }
+
// This can execute a workflow with firestore. Used for test, as datastore is old and stuff
// Too much work to move everything over alone, so won't touch it for now
// {
@@ -14538,8 +14700,8 @@ const AngularWorkflow = (defaultprops) => {
{
@@ -14547,6 +14709,8 @@ const AngularWorkflow = (defaultprops) => {
// workflow.public = false
// setWorkflow(workflow)
// setUpdate(Math.random());
+ toast("Unpublishing this workflow from the search engine. It will remain public until it is deleted, as it is a copy of the original workflow.")
+ unPublishWorkflow(workflow)
}}
>
Unpublish Workflow
@@ -15112,6 +15276,7 @@ const AngularWorkflow = (defaultprops) => {
+
{
Refresh Runs
+
{
) : (
-
- {
- setExecutionRunning(false);
- stop();
- getWorkflowExecution(props.match.params.key, "");
- setExecutionModalView(0);
- setLastExecution(executionData.execution_id);
- }}
+
+
- {
- setExecutionRunning(false);
- stop()
- }}
- >
-
-
- {
- const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
- const newitem = removeParam("execution_id", cursearch);
- navigate(curpath + newitem)
- setExecutionRunning(false);
- stop()
- }}
- >
- See more runs
-
-
-
+
{
+ setExecutionRunning(false);
+ stop();
+ getWorkflowExecution(props.match.params.key, "");
+ setExecutionModalView(0);
+ setLastExecution(executionData.execution_id);
+ }}
+ >
+ {
+ setExecutionRunning(false);
+ stop()
+ }}
+ >
+
+
+
+ {
+ const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
+ const newitem = removeParam("execution_id", cursearch);
+ navigate(curpath + newitem)
+ setExecutionRunning(false);
+ stop()
+ }}
+ >
+ See more runs
+
+
+
+
{
color="primary"
title="Explore logs for the workflow"
placement="top"
- style={{ zIndex: 50000 }}
+ style={{ zIndex: 50000, }}
>
{
- window.open(`/api/v1/workflows/search/${executionData.execution_id}`, "_blank")
+ toast("Opening logs in a new tab")
+
+ setTimeout(() => {
+ window.open(`/api/v1/workflows/search/${executionData.execution_id}`, "_blank")
+ }, 250)
}}
>
@@ -17855,42 +18028,45 @@ const AngularWorkflow = (defaultprops) => {
return null
}
+ var newrevision = JSON.parse(JSON.stringify(revision))
+
// Make unix timestamp into ISO timestamp in the format July 27th, 3:05 AM
// Format: July 27th, 3:05 AM
- console.log("Edited time: ", revision.edited)
+ //console.log("Edited time: ", revision.edited)
// Convert 1692128391 to valid timestamp
- const validTimestamp = revision.edited.toString().length === 10 ? revision.edited * 1000 : revision.edited
+ const validTimestamp = newrevision.edited.toString().length === 10 ? newrevision.edited * 1000 : newrevision.edited
const translatedDate = new Date(validTimestamp).toLocaleString('en-US', { month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
- var workflowStatus = revision.status !== undefined && revision.status !== null && revision.status !== "" ? revision.status : "test"
- if (revision.name !== undefined && revision.name !== null && revision.name !== "") {
- if (revision.name.toLowerCase().includes("test")) {
+ var workflowStatus = newrevision.status !== undefined && newrevision.status !== null && newrevision.status !== "" ? newrevision.status : "test"
+ if (newrevision.name !== undefined && newrevision.name !== null && newrevision.name !== "") {
+ if (newrevision.name.toLowerCase().includes("test")) {
workflowStatus = "test"
}
- if (revision.name.toLowerCase().includes("dev") || revision.name.toLowerCase().includes("staging") || revision.name.toLowerCase().includes("rollback")) {
+ if (newrevision.name.toLowerCase().includes("dev") || newrevision.name.toLowerCase().includes("staging") || newrevision.name.toLowerCase().includes("rollback")) {
workflowStatus = "dev"
}
- if (revision.name.toLowerCase().includes("prod") || revision.name.toLowerCase().includes("main")) {
+ if (newrevision.name.toLowerCase().includes("prod") || newrevision.name.toLowerCase().includes("main")) {
workflowStatus = "prod"
}
}
return (
{
- if (revision.edited === selectedRevision.edited) {
+ if (newrevision.edited === selectedRevision.edited) {
console.log("Same revision! No setting.")
return
}
+
// Should render if it's not the same as workflow.edited
- console.log("Clicked revision: ", revision)
+ console.log("Clicked revision: ", newrevision)
setLastSaved(false)
- setSelectedRevision(revision)
- setWorkflow(revision)
+ setSelectedRevision(newrevision)
+ setWorkflow(newrevision)
setSelectedAction({});
setSelectedApp({})
@@ -17910,7 +18086,7 @@ const AngularWorkflow = (defaultprops) => {
cy.removeListener("cxttap");
}
- setupGraph(revision)
+ setupGraph(newrevision)
// Re-adding cytoscape triggers
if (cy !== undefined && cy !== null) {
@@ -17936,8 +18112,20 @@ const AngularWorkflow = (defaultprops) => {
cy.on("free", "node", (e) => onNodeDragStop(e, selectedAction));
cy.on("cxttap", "node", (e) => onCtxTap(e));
+
+
+ if (selectedAction.id !== undefined && selectedAction.id !== null && selectedAction.id !== "") {
+ setTimeout(() => {
+ const foundaction = cy.$id(selectedAction.id)
+ if (foundaction !== undefined && foundaction !== null) {
+ foundaction.select()
+ }
+ }, 250)
+ }
}
+
+
// Need to run through graph setup with this one
}}>
diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx
index 21c2d513..e9a5c201 100755
--- a/frontend/src/views/Dashboard.jsx
+++ b/frontend/src/views/Dashboard.jsx
@@ -1,16 +1,11 @@
-import React, { useState, useEffect } from "react";
-import { useInterval } from "react-powerhooks";
-import AppFramework from "../components/AppFramework.jsx";
-import { makeStyles, } from "@mui/styles";
-// nodejs library that concatenates classes
-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 React, { useState, useEffect } from "react"
+import { useInterval } from "react-powerhooks"
+import AppFramework from "../components/AppFramework.jsx"
+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"
import { ToastContainer, toast } from "react-toastify"
import { parsedDatatypeImages } from "../components/AppFramework.jsx"
import { findSpecificApp } from "../components/AppFramework.jsx"
@@ -96,7 +91,8 @@ const useStyles = makeStyles({
const UsecaseListComponent = (props) => {
- const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, } = props
+ const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, getFramework, setFrameworkData, } = props
+
const [expandedIndex, setExpandedIndex] = useState(-1);
const [expandedItem, setExpandedItem] = useState(-1);
@@ -114,11 +110,13 @@ const UsecaseListComponent = (props) => {
const [firstLoad, setFirstLoad] = useState(true)
const [apps, setApps] = useState([])
+
const classes = useStyles();
let navigate = useNavigate();
const [mitreTags, setMitreTags] = useState([]);
+
const parseUsecase = (subcase) => {
const srcdata = findSpecificApp(frameworkData, subcase.type)
const dstdata = findSpecificApp(frameworkData, subcase.last)
@@ -137,13 +135,11 @@ const UsecaseListComponent = (props) => {
}
useEffect(() => {
- console.log("In frameworkData useEffect: frameworkData: ", frameworkData)
+ //console.log("Frameworkdata changed. Use to set inputUsecase: ", frameworkData, prevSubcase)
if (frameworkData === undefined || prevSubcase === undefined) {
return
}
- console.log("PAST!")
-
var parsedUsecase = inputUsecase
const subcase = parseUsecase(prevSubcase)
@@ -917,6 +913,9 @@ const UsecaseListComponent = (props) => {
description={inputUsecase.description}
apps={apps}
+ getAppFramework={getFramework}
+ //appSetupDone={appSetupDone}
+ //setAppSetupDone={setAppSetupDone}
/>
: null}
@@ -990,13 +989,15 @@ const UsecaseListComponent = (props) => {
}}>
@@ -1713,12 +1714,15 @@ const Dashboard = (props) => {
{treeKeys.length > 0 ?
diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx
index 2b0c1ebd..be99104c 100755
--- a/frontend/src/views/SettingsPage.jsx
+++ b/frontend/src/views/SettingsPage.jsx
@@ -13,6 +13,11 @@ import {
//import { useAlert
import { ToastContainer, toast } from "react-toastify"
+import { FileCopy, Visibility, VisibilityOff } from "@mui/icons-material";
+import IconButton from "@mui/material/IconButton";
+import { Tooltip } from "@mui/material";
+
+
const Settings = (props) => {
const { globalUrl, isLoaded, userdata, setUserData } = props;
//const alert = useAlert();
@@ -44,6 +49,17 @@ const Settings = (props) => {
const [userSettings, setUserSettings] = useState({});
+ const [showApiKey, setShowApiKey] = useState(false);
+ const [apiKeyCopied, setApiKeyCopied] = useState(false);
+
+ const handleCopyApiKey = () => {
+ navigator.clipboard.writeText(userSettings.apikey);
+ setApiKeyCopied(true);
+ setTimeout(() => {
+ setApiKeyCopied(false);
+ }, 2000);
+ }
+
/*
const [userdata.eth_info, setEthInfo] = useState(userdata.eth_info !== undefined && userdata.eth_info.account !== undefined && userdata.eth_info.account.length > 0 ? userdata.eth_info : {
"account": "",
@@ -467,7 +483,7 @@ const Settings = (props) => {
>
What is the API key used for?
- {
id="standard-required"
margin="normal"
variant="outlined"
- />
+ /> */}
+
+
+ setShowApiKey(!showApiKey)}
+ >
+ {showApiKey ? : }
+
+
+
+
+
+
+
+ >
+ ),
+ }}
+ color="primary"
+ value={showApiKey ? userSettings.apikey : '*'.repeat(36)} // Show API key if showApiKey is true, else show asterisks
+ required
+ disabled
+ fullWidth
+ placeholder="APIKEY"
+ id="standard-required"
+ margin="normal"
+ variant="outlined"
+ />
{
Submit password change
{passwordFormMessage}
-
- Creator Incentive Program
+ {isCloud && (
+ <>
+
+ Creator Incentive Program
+ >
+ )}
+
{isCloud ?
diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx
index 0cff5f0f..f453c98a 100755
--- a/frontend/src/views/Workflows.jsx
+++ b/frontend/src/views/Workflows.jsx
@@ -551,9 +551,7 @@ const Workflows = (props) => {
const [field1, setField1] = React.useState("");
const [field2, setField2] = React.useState("");
- const [downloadUrl, setDownloadUrl] = React.useState(
- "https://github.com/frikky/shuffle-workflows"
- );
+ const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/workflows")
const [downloadBranch, setDownloadBranch] = React.useState("master");
const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] =
React.useState(false);
@@ -924,7 +922,7 @@ const Workflows = (props) => {
>
- Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}?
+ Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId)?.name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}?
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
@@ -3630,7 +3628,7 @@ const Workflows = (props) => {
const handleGithubValidation = () => {
importWorkflowsFromUrl(downloadUrl);
setLoadWorkflowsModalOpen(false);
- };
+ }
const workflowDownloadModalOpen = loadWorkflowsModalOpen ? (
{
},
}}
onChange={(e) => setDownloadUrl(e.target.value)}
- placeholder="https://github.com/frikky/shuffle-apps"
+ placeholder="https://github.com/shuffle/workflows"
fullWidth
/>
- Branch (default value is "master"):
+ Branch (default value is "main"):