From 492c3c3258293a88c96c450fbf6f197c49688d93 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 31 Oct 2023 18:57:36 +0100 Subject: [PATCH 1/5] Frontend fixes for release --- frontend/src/components/AppFramework.jsx | 3 +- frontend/src/components/AppSearchButtons.jsx | 425 ++++-------------- frontend/src/components/AppSelection.jsx | 154 ++++--- frontend/src/components/ConfigureWorkflow.jsx | 6 +- frontend/src/components/Newsletter.jsx | 21 +- frontend/src/components/Priority.jsx | 6 +- .../src/components/WorkflowTemplatePopup.jsx | 35 +- frontend/src/views/AngularWorkflow.jsx | 45 +- frontend/src/views/AppCreator.jsx | 12 +- frontend/src/views/Dashboard.jsx | 81 ++-- frontend/src/views/Welcome.jsx | 7 +- frontend/src/views/Workflows.jsx | 10 +- functions/onprem/orborus/orborus.go | 12 +- 13 files changed, 324 insertions(+), 493 deletions(-) diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index 13f85fb1..ecff205f 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -39,7 +39,6 @@ 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") return null @@ -52,7 +51,7 @@ export const findSpecificApp = (framework, inputcategory) => { const category = inputcategory.toLowerCase().split(":")[0].trim() - console.log("findSpecificApp: ", category, framework) + //console.log("findSpecificApp: ", category, framework) if (category === "edr" || category === "eradication" || category === "edr & av") { if (framework["EDR & AV"] !== undefined && framework["EDR & AV"].name !== undefined) { return framework["EDR & AV"] diff --git a/frontend/src/components/AppSearchButtons.jsx b/frontend/src/components/AppSearchButtons.jsx index d739b96c..324da856 100644 --- a/frontend/src/components/AppSearchButtons.jsx +++ b/frontend/src/components/AppSearchButtons.jsx @@ -3,25 +3,26 @@ import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; import { useNavigate, Link } from 'react-router-dom'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; +import { Search as Searchicon, CloudQueue as CloudQueueicon, Code as Codeicon, Close as Closeicon, Folder as Foldericon, LibraryBooks as LibraryBooksicon } from '@mui/icons-material'; import aa from 'search-insights' -import DeleteIcon from '@mui/icons-material/Delete'; -import ShowChartIcon from '@mui/icons-material/ShowChart'; -import ExploreIcon from '@mui/icons-material/Explore'; -import LightbulbIcon from "@mui/icons-material/Lightbulb"; -import NewReleasesIcon from "@mui/icons-material/NewReleases"; -import ExtensionIcon from "@mui/icons-material/Extension"; -import EmailIcon from "@mui/icons-material/Email"; -import FingerprintIcon from '@mui/icons-material/Fingerprint'; +import Deleteicon from '@mui/icons-material/Delete'; +import ShowCharticon from '@mui/icons-material/ShowChart'; +import Exploreicon from '@mui/icons-material/Explore'; +import Lightbulbicon from "@mui/icons-material/Lightbulb"; +import NewReleasesicon from "@mui/icons-material/NewReleases"; +import Extensionicon from "@mui/icons-material/Extension"; +import Emailicon from "@mui/icons-material/Email"; +import Fingerprinticon from '@mui/icons-material/Fingerprint'; import AppSearch from "../components/Appsearch.jsx"; import { toast } from 'react-toastify'; +import { findSpecificApp } from "../components/AppFramework.jsx"; import { Zoom, Grid, Paper, TextField, Collapse, - IconButton, + iconButton, Avatar, ButtonBase, InputAdornment, @@ -35,367 +36,105 @@ import { } from '@mui/material'; const AppSearchButtons = (props) => { - const { userdata, globalUrl, appFramework, defaultSearch, finishedApps, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData } = props + const { userdata, globalUrl, appFramework, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData } = props + const ref = useRef() - const [moreButton, setMoreButton] = useState(false); let navigate = useNavigate(); - const sizing = moreButton ? 510 : 480; + const [isHover, setIsHover] = useState(false); + const [localSearchOpen, setLocalSearchOpen] = useState(false) + + if (appType === undefined) { + console.log("Apptype is required in AppSearchButtons") + return null; + } + const buttonWidth = 450; const buttonMargin = 10; - const bottomButtonStyle = { - borderRadius: 200, - marginTop: moreButton ? 44 : "", - height: 51, - width: 510, - fontSize: 16, - // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", - background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", - padding: "16px 24px", - // top: 20, - // margin: "auto", - textTransform: 'capitalize', - itemAlign: "center", - // marginTop: 25 - // marginLeft: "65px", - }; - const buttonStyle = { - flex: 1, - width: 224, - padding: 25, - margin: buttonMargin, - color: "var(--White-text, #F1F1F1)", - fontWeight: 400, - fontSize: 17, - background: "rgba(33, 33, 33, 1)", - textTransform: 'capitalize', - border: "1px solid rgba(33, 33, 33, 1)", - borderRadius: 8, - marginRight: 8, - }; - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const handleMouseEnter = () => { + setIsHover(true); + }; + + const handleMouseLeave = () => { + setIsHover(false); + }; - const mouseOver = (e) => { - e.target.style.border = "1px solid #f85a3e"; + + const foundApp = findSpecificApp(appFramework, appType) + if (foundApp === undefined || foundApp === null) { + console.log("AppSearchButtons: App not found in appFramework") + return null + } + + const icon = foundApp.large_image + let xsValue = 12; + if (index !== undefined && index !== 0) { + xsValue = 6; } - const mouseOut = (e) => { - e.target.style.border = "1px solid rgb(33, 33, 33)"; + + if (index === totalApps - 4 && totalApps % 2 === 1) { + xsValue = 12; + } + + if (index === totalApps - 1 || index === totalApps - 2 || index === totalApps - 3) { + xsValue = 4; } return ( - - - {/*Find your integrations!*/} - {/*
- -
-
- - -
-
- - -
- {moreButton ? ( -
- - - -
- ) - : -
- -
} */} -
+
-
} onClick={(event) => { - onNodeSelect("CASES"); - setDefaultSearch(discoveryData.label) + if (onNodeSelect !== undefined) { + onNodeSelect(appType); + } else { + setLocalSearchOpen(true) + } + + if (setDefaultSearch !== undefined) { + setDefaultSearch(appType) + } }} > -
- {AppImage === undefined || AppImage === undefined || - AppImage === null || AppImage === null || AppImage.length === 0 ? +
+ {AppImage === undefined || AppImage === null || AppImage.length === 0 ?
- +
- : } + : + + }
- Case Management + + {appType} + {appName === undefined || appName === undefined || appName === null || appName === null || appName.length === 0 ? "" : - {appName}} + {appName.split('_').join(' ')}}
-
+
) diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index d5f212df..c56b9d0f 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -50,6 +50,7 @@ const AppSelection = props => { const [newSelectedApp, setNewSelectedApp] = React.useState({}) const [finishedApps, setFinishedApps] = React.useState([]) const [appButtons, setAppButtons] = useState([]) + const [lastPosted, setLastPosted] = useState([]) const [apps, setApps] = useState([]) const [appName, setAppName] = React.useState(); const [moreButton, setMoreButton] = useState(false); @@ -75,7 +76,7 @@ const AppSelection = props => { body: JSON.stringify(data), credentials: "include", }) - .then((response) => { + .then(async (response) => { if (response.status !== 200) { console.log("Status not 200 for framework!"); } @@ -84,7 +85,13 @@ const AppSelection = props => { checkLogin() } - return response.json(); + let resp = response.json(); + let respAwaited = await resp; + + if (respAwaited.success === true) { + setLastPosted(data) + } + return resp; }) .then((responseJson) => { if (responseJson.success === false) { @@ -153,7 +160,6 @@ const AppSelection = props => { // if (setDiscoveryWrapper !== undefined) { // setDiscoveryWrapper({ id: label }); // } - if (isCloud) { ReactGA.event({ category: "welcome", @@ -161,41 +167,13 @@ const AppSelection = props => { label: "", }); } + setDiscoveryData(label) setSelectionOpen(true) setNewSelectedApp({}) setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase()) }; - useEffect(() => { - var tempApps = [] - if (tempApps.length === 0) { - const tempApps = - [{ - "description": newSelectedApp.description, - "id": newSelectedApp.objectID, - "large_image": newSelectedApp.image_url, - "name": newSelectedApp.name, - "type": discoveryData - }, - //{ - // // description: newSelectedApp.siem.description, - // id: newSelectedApp.siem.objectID, - // large_image: newSelectedApp.siem.image_url, - // name: newSelectedApp.siem.name, - // type: discoveryData.siem - // },{ - // // description: newSelectedApp.edr.description, - // id: newSelectedApp.edr.objectID, - // large_image: newSelectedApp.edr.image_url, - // name: newSelectedApp.edr.name, - // type: discoveryData.edr - // } - ] - setAppButtons(tempApps) - GetApps() - } - }, []) useEffect(() => { if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) { @@ -237,48 +215,94 @@ const AppSelection = props => { console.log("Selected app changed (effect)"); }, [newSelectedApp]); - const sizing = moreButton ? 510 : 480; + useEffect(() => { + var tempApps = [] + if (tempApps.length === 0) { + // Object.entries(appFramework).forEach(([key, value]) => { + // value.type = key; + // tempApps.push(value); + // }); + + // // Define the custom sorting order + // const customSortingOrder = ["CASES", "SIEM", "ENDPOINT", "INTEL", "EMAIL"]; + + const lastApps = {} + let endTypes = ["network", "assets", "iam"] + + if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) { + window.location.href = "/welcome" + return + } + + Object.entries(appFramework).forEach(([key, value]) => { + if (key.toLowerCase() === "other" || key.toLowerCase() === "communication") { + return + } + + value.type = key; + + if (endTypes.includes(value.type.toLowerCase())) { + lastApps[value.type] = value + return + } + + if (lastPosted.type === value.type) { + value = lastPosted + } + + tempApps.push(value); + }); + + tempApps.sort((a, b) => { + if (a.type.length > b.type.length) { + return -1; + } else if (a.type.length < b.type.length) { + return 1; + } + }); + + let lastType = lastPosted.type === undefined ? "" : lastPosted.type.toLowerCase() + + if (endTypes.includes(lastType)) { + lastApps[lastPosted.type] = lastPosted + } + + if (moreButton) { + tempApps.push(lastApps["network"]) + tempApps.push(lastApps["assets"]) + tempApps.push(lastApps["iam"]) + } + + setAppButtons(tempApps) + console.log("Updated appButtons: ", appButtons) + GetApps() + } + }, [lastPosted, moreButton]) + + // const sizing = moreButton ? 510 : 480; const buttonWidth = 450; const buttonMargin = 10; const bottomButtonStyle = { borderRadius: 200, marginTop: moreButton ? 44 : "", height: 51, - width: 510, + width: 500, fontSize: 16, - // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", padding: "16px 24px", - // top: 20, - // margin: "auto", textTransform: 'capitalize', itemAlign: "center", - // marginTop: 25 - // marginLeft: "65px", }; - const buttonStyle = { - flex: 1, - width: 224, - padding: 25, - margin: buttonMargin, - color: "var(--White-text, #F1F1F1)", - fontWeight: 400, - fontSize: 17, - background: "rgba(33, 33, 33, 1)", - textTransform: 'capitalize', - border: "1px solid rgba(33, 33, 33, 1)", - borderRadius: 8, - marginRight: 8, - }; - // console.log("appFramework",appFramework.cases.name) + return (
{selectionOpen ? ( @@ -311,7 +335,7 @@ const AppSelection = props => { style={{ flex: 1, // width: 224, - marginLeft: discoveryData === ('ERADICATION') ? 120 : 177, + marginLeft: discoveryData === ("communication") ? 112 : 200, width: "100%", marginBottom: 23, fontSize: 16, @@ -394,6 +418,7 @@ const AppSelection = props => { > Select the apps you work with and we will connect the for you. + {appButtons.map((appData, index) => { const appName = appData.name @@ -401,11 +426,12 @@ const AppSelection = props => { const appType = appData.type return ( - { setDiscoveryData={setDiscoveryData} setDefaultSearch={setDefaultSearch} apps={apps} + setMoreButton={setMoreButton} + moreButton={moreButton} /> ) })} +
+ {!moreButton ? ( +
+ { + setMoreButton(true) + }} + >See More Apps +
): ""}
+ +
{msg}
diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 3f167982..947a837d 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -31,10 +31,10 @@ 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) + //console.log("descsplit: ", descsplit) if (descsplit[1] === "") { const item = findSpecificApp(appFramework, descsplit[0]) - console.log("item: ", item) + //console.log("item: ", item) if (item !== null) { descsplit[1] = item.large_image descsplit[0] = descsplit[0].split(":")[0] @@ -45,7 +45,7 @@ const Priority = (props) => { if (descsplit[3] === "") { const item = findSpecificApp(appFramework, descsplit[2]) - console.log("item: ", item) + //console.log("item: ", item) if (item !== null) { descsplit[3] = item.large_image descsplit[2] = descsplit[2].split(":")[0] diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index f1367092..e7deea1d 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react"; import { toast } from "react-toastify" import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; +import AppSearchButtons from "../components/AppSearchButtons.jsx"; import { Button, Typography, @@ -26,7 +27,7 @@ import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx"; import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; const WorkflowTemplatePopup = (props) => { - const { userdata, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps } = props; + const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn } = props; const [isActive, setIsActive] = useState(false); const [isHovered, setIsHovered] = useState(false); @@ -36,6 +37,9 @@ const WorkflowTemplatePopup = (props) => { const [workflow, setWorkflow] = useState({}); const [appAuthentication, setAppAuthentication] = React.useState(undefined); + const [missingSource, setMissingSource] = React.useState(undefined) + const [missingDestination, setMissingDestination] = React.useState(undefined); + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); @@ -108,8 +112,10 @@ const WorkflowTemplatePopup = (props) => { } const loadAppAuth = () => { - if (userdata === undefined || userdata === null) { + // Check if it exists, and has keys + if (userdata === undefined || userdata === null || Object.keys(userdata).length === 0) { setErrorMessage("You need to be logged in to try usecases. Redirecting in 5 seconds...") + // Send the user to the login screen after 3 seconds setTimeout(() => { // Make it cancel if the state modalOpen changes @@ -174,6 +180,21 @@ const WorkflowTemplatePopup = (props) => { if (srcapp.includes(":default") || dstapp.includes(":default")) { toast("You need to select both a source and destination app before generating this workflow.") + + if (srcapp.includes(":default")) { + setMissingSource({ + "img": "https://shuffler.io/images/apps/" + srcapp + ".png", + "type": srcapp.split(":")[0], + }) + } + + if (dstapp.includes(":default")) { + setMissingDestination({ + "name": dstapp, + "img": "https://shuffler.io/images/apps/" + dstapp + ".png", + }) + } + return } @@ -322,6 +343,16 @@ const WorkflowTemplatePopup = (props) => {
} + {!isLoggedIn || missingSource === undefined ? null : +
+ +
+ } { > - +
+
+ +
) : null; diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 16dfdf23..eae7910f 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -929,9 +929,9 @@ const AppCreator = (defaultprops) => { } } - if (path === "/files/{file_id}/content") { - //console.log("FILE DOWNLOAD Method: ", path, method, methodvalue) - } + if (path === "/files/{file_id}/content") { + //console.log("FILE DOWNLOAD Method: ", path, method, methodvalue) + } // Typescript? I think not ;) @@ -2137,7 +2137,7 @@ const AppCreator = (defaultprops) => { } if (queryitem.name.toLowerCase() == "file_id") { - item.queries[querykey].name = "fileid" + item.queries[querykey].name = "fileid" continue; //skipped = true //break @@ -2525,8 +2525,8 @@ const AppCreator = (defaultprops) => { for (let authkey in extraAuth) { const curauth = extraAuth[authkey]; - if (curauth.name.toLowerCase() == "url") { - toast("Can't add extra auth with Name URL"); + if (curauth.name.length === 0 || curauth.name.toLowerCase() == "url") { + toast("Can't add extra auth with empty name or Name URL"); setAppBuilding(false); return; } diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index 94317c55..41443966 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -95,7 +95,9 @@ const useStyles = makeStyles({ -const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => { +const UsecaseListComponent = (props) => { + const { keys, userdata, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows, } = props + const [expandedIndex, setExpandedIndex] = useState(-1); const [expandedItem, setExpandedItem] = useState(-1); const [inputUsecase, setInputUsecase] = useState({}); @@ -165,12 +167,12 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData const parseUsecase = (subcase) => { - console.log("parseUsecase: ", subcase) + //console.log("parseUsecase: ", subcase) const srcdata = findSpecificApp(frameworkData, subcase.type) const dstdata = findSpecificApp(frameworkData, subcase.last) - console.log("srcdata: ", srcdata) - console.log("dstdata: ", dstdata) + //console.log("srcdata: ", srcdata) + //console.log("dstdata: ", dstdata) if (srcdata !== undefined && srcdata !== null) { subcase.srcimg = srcdata.large_image @@ -185,6 +187,7 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData return subcase } + const getUsecase = (subcase, index, subindex) => { subcase = parseUsecase(subcase) @@ -209,7 +212,7 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData return response.json(); }) .then((responseJson) => { - console.log("In responseJson for usecase: ", responseJson) + //console.log("In responseJson for usecase: ", responseJson) var parsedUsecase = responseJson if (responseJson.success === false) { @@ -229,19 +232,21 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData setExpandedItem(subindex) setTimeout(() => { - //console.log("Scroll!") const found = document.getElementById("selected_box"); if (found !== undefined && found !== null) { //console.log("FOUND!!") - found.scrollTo({ - top: 100, - behavior: "smooth", - }) + + //found.scrollTo({ + // top: 100, + // behavior: "smooth", + //}) + } else { + //console.log("NOT FOUND!!") } setFirstLoad(true) setSelectedWorkflows([]) - }, 100); + }, 100) }) .catch((error) => { //toast(error.toString()); @@ -784,7 +789,6 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData borderRadius: theme.palette.borderRadius, }} onChange={(event, newValue) => { - console.log("CLICK: ", newValue) //handleWorkflowSelectionUpdate({ target: { value: newValue} }) //setSelectedWorkflows= //var newvalue = [] @@ -801,7 +805,6 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) { const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name) if (filtered !== undefined && filtered !== null) { - //console.log("Removing: ", workflows[key].name, workflows[key]) workflows[key]["usecase_ids"] = filtered setWorkflow(workflows[key]) @@ -813,19 +816,16 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) { workflows[key]["usecase_ids"] = [subcase.name] - console.log("Setting: ", workflows[key].name) setWorkflow(workflows[key]) } else if (!workflows[key]["usecase_ids"].includes(subcase.name)) { workflows[key]["usecase_ids"].push(subcase.name) - console.log("Adding: ", workflows[key].name) setWorkflow(workflows[key]) } } setWorkflows(workflows) - console.log("New: ", newValue) setSelectedWorkflows(newValue) //setUpdate(Math.random()) }} @@ -888,6 +888,8 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData Try it out: { // This is the start of a dashboard that can be used. // What data do we fill in here? Idk const Dashboard = (props) => { - const { globalUrl, isLoggedIn } = props; + const { globalUrl, isLoggedIn, userdata } = props; //const alert = useAlert(); const [bigChartData, setBgChartData] = useState("data1"); const [dayAmount, setDayAmount] = useState(7); @@ -1176,7 +1178,7 @@ const Dashboard = (props) => { const foundQuery2 = params["selected_object"] if (foundQuery2 !== null && foundQuery2 !== undefined) { - console.log("Got selected_object: ", foundQuery2) + //console.log("Got selected_object: ", foundQuery2) const queryName = foundQuery2.toLowerCase().replaceAll("_", " ") // Waiting a bit for it to render @@ -1184,6 +1186,15 @@ const Dashboard = (props) => { const foundItem = document.getElementById(queryName) if (foundItem !== undefined && foundItem !== null) { foundItem.click() + // Scroll to it + + setTimeout(() => { + foundItem.scrollIntoView({ + behavior: "smooth", + block: "center", + inline: "center" + }) + }, 100) } else { //console.log("Couldn't find item with name ", queryName) } @@ -1194,12 +1205,12 @@ const Dashboard = (props) => { useEffect(() => { if (usecases.length > 0) { - console.log(usecases) + //console.log(usecases) checkSelectedParams() } }, [usecases]) - const getFramework = () => { + const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", headers: { @@ -1216,20 +1227,29 @@ const Dashboard = (props) => { return response.json(); }) .then((responseJson) => { - if (responseJson.success === false) { - if (responseJson.reason !== undefined) { - //toast("Failed loading: " + responseJson.reason) - } else { - //toast("Failed to load framework for your org.") - } - } else { - setFrameworkData(responseJson) + if (responseJson.success === false) { + const preparedData = { + "siem": findSpecificApp({}, "SIEM"), + "communication": findSpecificApp({}, "COMMUNICATION"), + "assets": findSpecificApp({}, "ASSETS"), + "cases": findSpecificApp({}, "CASES"), + "network": findSpecificApp({}, "NETWORK"), + "intel": findSpecificApp({}, "INTEL"), + "edr": findSpecificApp({}, "EDR"), + "iam": findSpecificApp({}, "IAM"), + "email": findSpecificApp({}, "EMAIL"), } - }) + + console.log("Got error for framework! ", preparedData) + setFrameworkData(preparedData) + } else { + setFrameworkData(responseJson) + } + }) .catch((error) => { toast(error.toString()); }) - } + } const getAvailableWorkflows = () => { fetch(globalUrl + "/api/v1/workflows", { @@ -1659,6 +1679,7 @@ const Dashboard = (props) => { : null} { } }, [activeStep]) - const isCloud = - window.location.host === "localhost:3002" || - window.location.host === "shuffler.io"; - + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const [steps, setSteps] = useState([ "Help us get to know you", "Find your Apps", @@ -512,7 +509,7 @@ const Welcome = (props) => { navigate("/workflows?message=Skipped intro") }}> - + Experienced diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 67998211..bf32b52e 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -530,6 +530,7 @@ export const validateJson = (showResult) => { const Workflows = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props; + document.title = "Shuffle - Workflows"; let navigate = useNavigate(); @@ -3419,11 +3420,11 @@ const Workflows = (props) => { > { {foundPriority != null && workflows.length < 6 ? 0 { + env = append(env, fmt.Sprintf("SHUFFLE_SKIPSSL_VERIFY=%s", os.Getenv("SHUFFLE_SKIPSSL_VERIFY"))) + } + + if len(os.Getenv("SHUFFLE_DEBUG_MEMORY")) > 0 { + env = append(env, fmt.Sprintf("SHUFFLE_DEBUG_MEMORY=%s", os.Getenv("SHUFFLE_DEBUG_MEMORY"))) + } + err = deployWorker(workerImage, containerName, env, execution) zombiecounter += 1 if err == nil { From cf4aa71f8094991bc98c10ade52e4d280bfb7bdf Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 6 Nov 2023 08:32:58 +0100 Subject: [PATCH 2/5] A tons of minor fixes for the onboarding flow and search systems --- frontend/src/components/AppFramework.jsx | 2 +- frontend/src/components/AppGrid.jsx | 4 +- frontend/src/components/AppSearchButtons.jsx | 159 ++++++++- frontend/src/components/AppSelection.jsx | 306 ++++++++++-------- frontend/src/components/Appsearch.jsx | 29 +- frontend/src/components/Billing.jsx | 233 ++++++++++++- frontend/src/components/CreatorGrid.jsx | 4 +- frontend/src/components/ExploreWorkflow.jsx | 14 +- frontend/src/components/Priorities.jsx | 39 +++ frontend/src/components/Priority.jsx | 31 +- frontend/src/components/WelcomeForm2.jsx | 29 +- .../src/components/WorkflowTemplatePopup.jsx | 88 +++-- frontend/src/views/Admin.jsx | 51 ++- frontend/src/views/AngularWorkflow.jsx | 5 +- frontend/src/views/Dashboard.jsx | 42 +-- frontend/src/views/Search.jsx | 37 +-- frontend/src/views/Welcome.jsx | 90 ++---- functions/onprem/orborus/orborus.go | 3 + 18 files changed, 799 insertions(+), 367 deletions(-) diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index ecff205f..383553fd 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -64,7 +64,7 @@ export const findSpecificApp = (framework, inputcategory) => { description: "", id: "", } - } else if (category === "communication") { + } else if (category === "communication" || category === "comms") { if (framework["Comms"] !== undefined && framework["Comms"].name !== undefined) { return framework["Comms"] } diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index 2d0e9d3d..5bb08119 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -31,7 +31,7 @@ import { const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") const AppGrid = props => { - const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata } = props + const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, isHeader } = props const isCloud = window.location.host === "localhost:3002" || @@ -167,7 +167,7 @@ const AppGrid = props => { backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", border: `1px solid ${innerColor}`, - padding: 15, + padding: isHeader ? null : 15, cursor: "pointer", position: "relative", minHeight: 116, diff --git a/frontend/src/components/AppSearchButtons.jsx b/frontend/src/components/AppSearchButtons.jsx index 324da856..2c85eb89 100644 --- a/frontend/src/components/AppSearchButtons.jsx +++ b/frontend/src/components/AppSearchButtons.jsx @@ -3,7 +3,7 @@ import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; import { useNavigate, Link } from 'react-router-dom'; -import { Search as Searchicon, CloudQueue as CloudQueueicon, Code as Codeicon, Close as Closeicon, Folder as Foldericon, LibraryBooks as LibraryBooksicon } from '@mui/icons-material'; +import { Search as Searchicon, CloudQueue as CloudQueueicon, Code as Codeicon, Close as Closeicon, Folder as Foldericon, LibraryBooks as LibraryBooksicon, Delete as DeleteIcon, Close as CloseIcon, } from '@mui/icons-material'; import aa from 'search-insights' import Deleteicon from '@mui/icons-material/Delete'; import ShowCharticon from '@mui/icons-material/ShowChart'; @@ -33,16 +33,37 @@ import { ListItem, ListItemAvatar, ListItemText, + IconButton, } from '@mui/material'; const AppSearchButtons = (props) => { - const { userdata, globalUrl, appFramework, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData } = props + const { userdata, globalUrl, appFramework, finishedApps, appType, totalApps, index, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData, checkLogin, setMissing, } = props const ref = useRef() let navigate = useNavigate(); const [isHover, setIsHover] = useState(false); const [localSearchOpen, setLocalSearchOpen] = useState(false) + const [newSelectedApp, setNewSelectedApp] = useState(undefined) + + useEffect(() => { + console.log("AppSearchButtons: newSelectedApp: " + JSON.stringify(newSelectedApp)) + + if (newSelectedApp !== undefined && setMissing != undefined) { + console.log("AppSearchButtons: setMissing is defined!") + + const submitAppFramework = { + "description": newSelectedApp.description, + "id": newSelectedApp.objectID, + "name": newSelectedApp.name, + "type": appType, + "large_image": newSelectedApp.image_url, + } + + setFrameworkItem(submitAppFramework) + setMissing(newSelectedApp) + } + }, [newSelectedApp]) if (appType === undefined) { console.log("Apptype is required in AppSearchButtons") @@ -63,10 +84,54 @@ const AppSearchButtons = (props) => { const foundApp = findSpecificApp(appFramework, appType) if (foundApp === undefined || foundApp === null) { - console.log("AppSearchButtons: App not found in appFramework") + console.log("AppSearchButtons: App not found in appFramework: " + appType) return null } + const setFrameworkItem = (data) => { + + 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!"); + } + + if (checkLogin !== undefined) { + checkLogin() + } + + return response; + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Failed updating: " + responseJson.reason) + } else { + toast("Failed to update framework for your org.") + + } + } + //setFrameworkLoaded(true) + //setFrameworkData(responseJson) + }) + .catch((error) => { + if (checkLogin !== undefined) { + checkLogin() + } + + toast(error.toString()); + //setFrameworkLoaded(true) + }) + } + const icon = foundApp.large_image let xsValue = 12; if (index !== undefined && index !== 0) { @@ -86,6 +151,94 @@ const AppSearchButtons = (props) => { onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > + {localSearchOpen ? ( +
+
+
+ {discoveryData} +
+
+ + { + setLocalSearchOpen(false) + }} + > + + + + + { + e.preventDefault(); + setLocalSearchOpen(false) + setDefaultSearch("") + const submitDeletedApp = { + "description": "", + "id": "remove", + "name": "", + "type": discoveryData + } + setFrameworkItem(submitDeletedApp) + setNewSelectedApp({}) + setTimeout(() => { + setDiscoveryData({}) + setFrameworkItem(submitDeletedApp) + //setNewSelectedApp({}) + }, 1000) + //setAppName(discoveryData.cases.name) + }} + > + + + +
+
+
+ +
+ ) : null}
diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index c56b9d0f..9dafa6b3 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from "react"; import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; import { useNavigate, Link } from 'react-router-dom'; - +import {isMobile} from "react-device-detect"; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; import aa from 'search-insights' import DeleteIcon from '@mui/icons-material/Delete'; @@ -60,6 +60,117 @@ const AppSelection = props => { let navigate = useNavigate(); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + useEffect(() => { + if (newSelectedApp === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) { + return + } + + const submitNewApp = { + description: newSelectedApp.description, + id: newSelectedApp.objectID, + large_image: newSelectedApp.image_url, + name: newSelectedApp.name, + type: discoveryData + } + + if (discoveryData === "CASES") { + appFramework.cases = submitNewApp + } + else if (discoveryData === "SIEM") { + appFramework.siem = submitNewApp + } + else if (discoveryData === "ERADICATION") { + appFramework.edr = submitNewApp + } + else if (discoveryData === "INTEL") { + appFramework.intel = submitNewApp + } + else if (discoveryData === "EMAIL") { + appFramework.communication = submitNewApp + } + else if (discoveryData === "NETWORK") { + appFramework.network = submitNewApp + } + else if (discoveryData === "ASSETS") { + appFramework.assets = submitNewApp + } + else if (discoveryData === "IAM") { + appFramework.iam = submitNewApp + } + setFrameworkItem(submitNewApp); + setSelectionOpen(false); + console.log("Selected app changed (effect)"); + }, [newSelectedApp]); + + useEffect(() => { + var tempApps = [] + if (tempApps.length === 0) { + // Object.entries(appFramework).forEach(([key, value]) => { + // value.type = key; + // tempApps.push(value); + // }); + + // // Define the custom sorting order + // const customSortingOrder = ["CASES", "SIEM", "ENDPOINT", "INTEL", "EMAIL"]; + + const lastApps = {} + let endTypes = ["network", "assets", "iam"] + + if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) { + //window.location.href = "/welcome" + return + } + + + Object.entries(appFramework).forEach(([key, value]) => { + if (key.toLowerCase() === "other" || key.toLowerCase() === "communication") { + return + } + + value.type = key; + + if (endTypes.includes(value.type.toLowerCase())) { + lastApps[value.type] = value + return + } + + if (lastPosted.type === value.type) { + value = lastPosted + } + + tempApps.push(JSON.parse(JSON.stringify(value))); + }); + + tempApps.sort((a, b) => { + if (a.type.length > b.type.length) { + return -1; + } else if (a.type.length < b.type.length) { + return 1; + } + }); + + let lastType = lastPosted.type === undefined ? "" : lastPosted.type.toLowerCase() + + if (endTypes.includes(lastType)) { + lastApps[lastPosted.type] = lastPosted + } + + if (moreButton) { + tempApps.push(JSON.parse(JSON.stringify(lastApps["network"]))) + tempApps.push(JSON.parse(JSON.stringify(lastApps["assets"]))) + tempApps.push(JSON.parse(JSON.stringify(lastApps["iam"]))) + } + + setAppButtons(tempApps) + console.log("Updated appButtons: ", appButtons) + GetApps() + } + }, [lastPosted, moreButton]) + + if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) { + //window.location.href = "/welcome" + return null + } const setFrameworkItem = (data) => { console.log("Setting framework item: ", data, isCloud) @@ -157,9 +268,6 @@ const AppSelection = props => { } const onNodeSelect = (label) => { - // if (setDiscoveryWrapper !== undefined) { - // setDiscoveryWrapper({ id: label }); - // } if (isCloud) { ReactGA.event({ category: "welcome", @@ -168,116 +276,16 @@ const AppSelection = props => { }); } + console.log("NODESELECT: ", label) + setDiscoveryData(label) setSelectionOpen(true) - setNewSelectedApp({}) setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase()) + + setNewSelectedApp(undefined) }; - - useEffect(() => { - if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) { - return - } - const submitNewApp = { - description: newSelectedApp.description, - id: newSelectedApp.objectID, - large_image: newSelectedApp.image_url, - name: newSelectedApp.name, - type: discoveryData - } - if (discoveryData === "CASES") { - appFramework.cases = submitNewApp - } - else if (discoveryData === "SIEM") { - appFramework.siem = submitNewApp - } - else if (discoveryData === "ERADICATION") { - appFramework.edr = submitNewApp - } - else if (discoveryData === "INTEL") { - appFramework.intel = submitNewApp - } - else if (discoveryData === "EMAIL") { - appFramework.communication = submitNewApp - } - else if (discoveryData === "NETWORK") { - appFramework.network = submitNewApp - } - else if (discoveryData === "ASSETS") { - appFramework.assets = submitNewApp - } - else if (discoveryData === "IAM") { - appFramework.iam = submitNewApp - } - setFrameworkItem(submitNewApp); - setSelectionOpen(false); - console.log("Selected app changed (effect)"); - }, [newSelectedApp]); - - useEffect(() => { - var tempApps = [] - if (tempApps.length === 0) { - // Object.entries(appFramework).forEach(([key, value]) => { - // value.type = key; - // tempApps.push(value); - // }); - - // // Define the custom sorting order - // const customSortingOrder = ["CASES", "SIEM", "ENDPOINT", "INTEL", "EMAIL"]; - - const lastApps = {} - let endTypes = ["network", "assets", "iam"] - - if (appFramework === undefined || appFramework === null || Object.keys(appFramework).length === 0) { - window.location.href = "/welcome" - return - } - - Object.entries(appFramework).forEach(([key, value]) => { - if (key.toLowerCase() === "other" || key.toLowerCase() === "communication") { - return - } - - value.type = key; - - if (endTypes.includes(value.type.toLowerCase())) { - lastApps[value.type] = value - return - } - - if (lastPosted.type === value.type) { - value = lastPosted - } - - tempApps.push(value); - }); - - tempApps.sort((a, b) => { - if (a.type.length > b.type.length) { - return -1; - } else if (a.type.length < b.type.length) { - return 1; - } - }); - - let lastType = lastPosted.type === undefined ? "" : lastPosted.type.toLowerCase() - - if (endTypes.includes(lastType)) { - lastApps[lastPosted.type] = lastPosted - } - - if (moreButton) { - tempApps.push(lastApps["network"]) - tempApps.push(lastApps["assets"]) - tempApps.push(lastApps["iam"]) - } - - setAppButtons(tempApps) - console.log("Updated appButtons: ", appButtons) - GetApps() - } - }, [lastPosted, moreButton]) + // const sizing = moreButton ? 510 : 480; const buttonWidth = 450; @@ -286,7 +294,7 @@ const AppSelection = props => { borderRadius: 200, marginTop: moreButton ? 44 : "", height: 51, - width: 500, + width: isMobile ? 250 : 500, fontSize: 16, background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", padding: "16px 24px", @@ -301,14 +309,15 @@ const AppSelection = props => { // minHeight: sizing, // maxHeight: sizing, marginTop: 10, - width: 500, - marginBottom: 25 + width: isMobile ? 380 : 500, + marginBottom: 25, + textAlign: isMobile ? "center" : null, }} > {selectionOpen ? (
{ variant="h4" style={{ marginLeft: 8, - marginTop: 40, + marginTop: isMobile ? null : 40, marginRight: 30, marginBottom: 0, }} @@ -419,42 +428,59 @@ const AppSelection = props => { Select the apps you work with and we will connect the for you. - {appButtons.map((appData, index) => { + {appButtons.map((appData, index) => { + // This is here due to a memory issue with setting apps properly + if (appData.id === "remove") { + console.log("Removed as appdata is overridden: ", appData) - const appName = appData.name - const AppImage = appData.large_image - const appType = appData.type + appData = { + "count": 0, + "description": "", + "id": "", + "large_image": "", + "name": "", + "type": appData.type, + } + } - return ( - - ) - })} + const appName = appData.name + const AppImage = appData.large_image + const appType = appData.type + + return ( + + ) + })}
{!moreButton ? ( -
+
{ setMoreButton(true) + + setTimeout(() => { + navigate("/welcome?tab=2") + }, 250) }} >See More Apps
): ""} -
+
+
+ +
- + {top_text} + {isCloud && highlight === true ? + + { + setSignatureOpen(true) + }} + > + + + + : null}
@@ -320,15 +460,61 @@ const Billing = (props) => { } - if (feature.includes("Licensed Worker: ")) { + if (feature.includes("Worker License: ")) { + const fieldId = "webhook_uri_field_"+index parsedFeature = - - Download the licensed worker - + + + Use the {feature.split("Worker License: ")[0]} Worker + + {}} + InputProps={{ + endAdornment: + + { + var copyText = document.getElementById(fieldId); + if (copyText !== undefined && copyText !== null) { + console.log("NAVIGATOR: ", navigator); + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(copyText.value); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999 + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + toast("Copied Webhook URL"); + } else { + console.log("Couldn't find webhook URI field: ", copyText); + } + }} + edge="end" + > + + + + }} + fullWidth + /> + } return ( @@ -342,7 +528,7 @@ const Billing = (props) => { : null}
- {(highlight === true && subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name.includes("Scale") ? + {(highlight === true && (subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name === "Open Source") ? {subscription.name.includes("Scale") ? @@ -351,6 +537,18 @@ const Billing = (props) => { "You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit." } + {/*isCloud ? + + : null*/} : null} @@ -692,6 +890,7 @@ const Billing = (props) => {
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? { : !isCloud ? { highlight={true} /> { .map((sub, index) => { return ( { stripeKey={stripeKey} selectedOrganization={selectedOrganization} subscription={sub} + highlight={true} /> ) }) diff --git a/frontend/src/components/CreatorGrid.jsx b/frontend/src/components/CreatorGrid.jsx index 56b80786..ce6a197f 100644 --- a/frontend/src/components/CreatorGrid.jsx +++ b/frontend/src/components/CreatorGrid.jsx @@ -39,7 +39,7 @@ import { const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const CreatorGrid = props => { - const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props + const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, isHeader } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs //const [apps, setApps] = React.useState([]); @@ -165,7 +165,7 @@ const CreatorGrid = props => { return ( - + diff --git a/frontend/src/components/ExploreWorkflow.jsx b/frontend/src/components/ExploreWorkflow.jsx index 95b464e7..6333d673 100644 --- a/frontend/src/components/ExploreWorkflow.jsx +++ b/frontend/src/components/ExploreWorkflow.jsx @@ -4,6 +4,7 @@ import ReactGA from 'react-ga4'; import 'react-alice-carousel/lib/alice-carousel.css'; import TrendingFlatIcon from '@mui/icons-material/TrendingFlat'; import theme from '../theme.jsx'; +import {isMobile} from "react-device-detect"; import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp'; import { findSpecificApp } from "../components/AppFramework.jsx" import { @@ -40,7 +41,7 @@ import { useNavigate, Link } from "react-router-dom"; import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"; const ExploreWorkflow = (props) => { - const { userdata, globalUrl, appFramework } = props + const { userdata, globalUrl, appFramework, isLoggedIn, } = props const [activeUsecases, setActiveUsecases] = useState(0); const [modalOpen, setModalOpen] = React.useState(false); const [suggestedUsecases, setSuggestedUsecases] = useState([]) @@ -291,7 +292,7 @@ const ExploreWorkflow = (props) => { return (
{modalView} - + Start using workflows @@ -300,7 +301,7 @@ const ExploreWorkflow = (props) => {
-
+
{suggestedUsecases.length === 0 && usecasesSet ? @@ -330,7 +331,10 @@ const ExploreWorkflow = (props) => { return ( { fullWidth style={{ borderRadius: 200, height: 51, - width: 464, + width: isMobile? null : 464, fontSize: 16, padding: "16px 24px", margin: "auto", @@ -368,7 +372,7 @@ const ExploreWorkflow = (props) => {
- + Explore usecases diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index f003540c..b588fdf8 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -18,11 +18,49 @@ const Priorities = (props) => { const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, } = props; const [showDismissed, setShowDismissed] = React.useState(false); const [showRead, setShowRead] = React.useState(false); + const [appFramework, setAppFramework] = React.useState({}); + + useEffect(() => { + getFramework() + }, []) if (userdata === undefined || userdata === null) { return } + const getFramework = () => { + fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + setAppFramework({}) + if (responseJson.reason !== undefined) { + //toast("Failed loading: " + responseJson.reason) + } else { + //toast("Failed to load framework for your org.") + } + } else { + setAppFramework(responseJson) + } + }) + .catch((error) => { + console.log("err in framework: ", error.toString()); + }) + } + return (

Suggestions

@@ -62,6 +100,7 @@ const Priorities = (props) => { checkLogin={checkLogin} setAdminTab={setAdminTab} setCurTab={setCurTab} + appFramework={appFramework} /> ) }) diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 947a837d..13c278d3 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -27,31 +27,42 @@ const Priority = (props) => { const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); - var realigned = false + var realignedSrc = false + var realignedDst = false let newdescription = priority.description const descsplit = priority.description.split("&") if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) { - //console.log("descsplit: ", descsplit) + console.log("descsplit: ", descsplit) if (descsplit[1] === "") { const item = findSpecificApp(appFramework, descsplit[0]) - //console.log("item: ", item) + if (item !== null) { descsplit[1] = item.large_image + + console.log("DESCSPLIT name: ", descsplit[0]) + + if (descsplit[0].includes(":default")) { + realignedSrc = true + } + descsplit[0] = descsplit[0].split(":")[0] } - - realigned = true } if (descsplit[3] === "") { const item = findSpecificApp(appFramework, descsplit[2]) //console.log("item: ", item) + realignedDst = true if (item !== null) { descsplit[3] = item.large_image + + if (descsplit[2].includes(":default")) { + realignedDst = true + } + descsplit[2] = descsplit[2].split(":")[0] } - - realigned = true + } newdescription = descsplit.join("&") @@ -101,6 +112,8 @@ const Priority = (props) => { } + const srcSize = realignedSrc ? 35 : 30 + const dstSize = realignedDst ? 35 : 30 return (
@@ -112,7 +125,7 @@ const Priority = (props) => { {priority.type === "usecase" && priority.description.includes("&") ? - {priority.name} + {priority.name} {newdescription.split("&")[0]} @@ -120,7 +133,7 @@ const Priority = (props) => { {newdescription.split("&").length > 3 ? - {priority.name+"2"} + {priority.name+"2"} {newdescription.split("&")[2]} diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index f0b71273..3c9200e0 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -4,7 +4,7 @@ import Checkbox from "@mui/material/Checkbox"; import AliceCarousel from "react-alice-carousel"; import "react-alice-carousel/lib/alice-carousel.css"; - +import {isMobile} from "react-device-detect"; import SearchIcon from "@mui/icons-material/Search"; import EmailIcon from "@mui/icons-material/Email"; import NewReleasesIcon from "@mui/icons-material/NewReleases"; @@ -79,6 +79,7 @@ const WelcomeForm = (props) => { selectionOpen, setSelectionOpen, checkLogin, + isLoggedIn, } = props; const [moreButton, setMoreButton] = useState(false); const ref = useRef() @@ -449,19 +450,6 @@ const WelcomeForm = (props) => { } }; - const handleSkip = () => { - setclickdiff(240); - if (!isStepOptional(activeStep)) { - throw new Error("You can't skip a step that isn't optional."); - } - setActiveStep((prevActiveStep) => prevActiveStep + 1); - setSkipped((prevSkipped) => { - const newSkipped = new Set(prevSkipped.values()); - newSkipped.add(activeStep); - return newSkipped; - }); - }; - const handleReset = () => { setActiveStep(0); }; @@ -652,6 +640,7 @@ const WelcomeForm = (props) => { ); case 1: return ( + appFramework === undefined || appFramework === null ? null : { case 2: return ( -
+
-
+
@@ -692,13 +682,6 @@ const WelcomeForm = (props) => { const extraHeight = isCloud ? -7 : 0; return (
- {/*selectionOpen ? - - : null*/}
{activeStep === steps.length ? (
diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index e7deea1d..9b283da9 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -4,6 +4,7 @@ import { toast } from "react-toastify" 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 { Button, Typography, @@ -27,7 +28,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 } = props; + const { userdata, appFramework, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps, isLoggedIn, isHomePage } = props; const [isActive, setIsActive] = useState(false); const [isHovered, setIsHovered] = useState(false); @@ -40,14 +41,18 @@ const WorkflowTemplatePopup = (props) => { const [missingSource, setMissingSource] = React.useState(undefined) const [missingDestination, setMissingDestination] = React.useState(undefined); + useEffect(() => { + console.log("Source & Dest check:", missingSource, missingDestination) + }, [missingSource, missingDestination]) + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); - const imagestyleWrapper = { + const imagestyleWrapper = { height: 40, width: 40, borderRadius: 40, - border: "1px solid rgba(255,255,255,0.3)", + border: isHomePage ? null : "1px solid rgba(255,255,255,0.3)", overflow: "hidden", display: "flex", } @@ -56,7 +61,7 @@ const WorkflowTemplatePopup = (props) => { height: 40, width: 40, borderRadius: 40, - border: "1px solid rgba(255,255,255,0.3)", + border: isHomePage ? null : "1px solid rgba(255,255,255,0.3)", overflow: "hidden", display: "flex", } @@ -65,13 +70,13 @@ const WorkflowTemplatePopup = (props) => { height: 40, width: 40, borderRadius: 40, - border: "1px solid rgba(255,255,255,0.3)", + border: isHomePage ? null : "1px solid rgba(255,255,255,0.3)", overflow: "hidden", } const imagestyleDefault = { display: "block", - marginLeft: 11, + marginLeft: 12, marginTop: 11, height: 35, width: "auto", @@ -168,6 +173,7 @@ const WorkflowTemplatePopup = (props) => { }); } + const getGeneratedWorkflow = () => { // POST // https://shuffler.io/api/v1/workflows/merge @@ -183,15 +189,13 @@ const WorkflowTemplatePopup = (props) => { if (srcapp.includes(":default")) { setMissingSource({ - "img": "https://shuffler.io/images/apps/" + srcapp + ".png", "type": srcapp.split(":")[0], }) } if (dstapp.includes(":default")) { setMissingDestination({ - "name": dstapp, - "img": "https://shuffler.io/images/apps/" + dstapp + ".png", + "type": dstapp.split(":")[0], }) } @@ -200,7 +204,6 @@ const WorkflowTemplatePopup = (props) => { setWorkflowLoading(true) - // FIXME: Remove hardcoding here after testing, and user srcapp/dstapp const newsrcapp = srcapp const newdstapp = dstapp @@ -228,15 +231,21 @@ const WorkflowTemplatePopup = (props) => { }) .then((response) => { if (response.status !== 200) { - console.log("Status not 200 for framework!"); + //console.log("Status not 200 for framework!"); } setWorkflowLoading(false) return response.json(); }) .then((responseJson) => { + if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "" && responseJson.name !== undefined && responseJson.name !== null && responseJson.name !== "") { + console.log("Success in workflow template (prebuilt): ", responseJson); + setWorkflow(responseJson) + return + } + if (responseJson.success === false) { - console.log("Error in workflow template: ", responseJson.error); + //console.log("Error in workflow template: ", responseJson.error); setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.") @@ -290,9 +299,9 @@ const WorkflowTemplatePopup = (props) => { style: { backgroundColor: "black", color: "white", - minWidth: 700, - maxWidth: 700, - paddingTop: 75, + minWidth: isHomePage ? null : isMobile ? 300 : 700, + maxWidth: isHomePage ? null : isMobile ? 300 : 700, + paddingTop: isMobile ? null : 75, itemAlign: "center", }, }} @@ -311,8 +320,8 @@ const WorkflowTemplatePopup = (props) => { > - - + + Configure Workflow @@ -343,16 +352,41 @@ const WorkflowTemplatePopup = (props) => {
} - {!isLoggedIn || missingSource === undefined ? null : -
+ + {isLoggedIn && (missingSource !== undefined || missingDestination !== undefined) ? + + {"The following app category is required generate this workflow: "} + + : null} + + {(missingSource !== undefined) ? +
- } + : null} + + {(missingDestination !== undefined) ? +
+ +
+ : null} + { if (title.length > maxlength) { parsedTitle = title.substring(0, maxlength) + "..." } - + console.log("isHomePage", isHomePage) parsedTitle = parsedTitle.replaceAll("_", " ") const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : "" return ( -
+
{ }
- + {parsedTitle} - + {parsedDescription}
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 346609f4..1ef82ba3 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -980,6 +980,22 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user leads.push("customer") } + if (responseJson.lead_info.old_customer) { + leads.push("old customer") + } + + if (responseJson.lead_info.tech_partner) { + leads.push("tech partner") + } + + if (responseJson.lead_info.creator) { + leads.push("creator") + } + + if (responseJson.lead_info.opensource) { + leads.push("open source") + } + if (responseJson.lead_info.demo_done) { leads.push("demo done") } @@ -2412,7 +2428,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user renderValue={(selected) => selected.join(', ')} MenuProps={MenuProps} > - {["contacted", "lead", "pov", "demo done", "customer", "student", "internal"].map((name) => ( + {["contacted", "lead", "pov", "demo done", "customer", "open source", "student", "internal", "old customer", "creator", "tech partner"].map((name) => ( -1} /> @@ -3000,7 +3016,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user curTab === 1 ? (
-

User management

+

User Management

Add, edit, block or change passwords.{" "} + {users === undefined || users === null @@ -3091,6 +3111,24 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user bgColor = "#1f2023"; } + const timeNow = new Date().getTime(); + + // Get the highest timestamp in data.login_info + var lastLogin = "N/A" + if (data.login_info !== undefined && data.login_info !== null) { + var loginInfo = 0 + for (var i = 0; i < data.login_info.length; i++) { + if (data.login_info[i].timestamp > loginInfo) { + loginInfo = data.login_info[i].timestamp + } + } + + if (loginInfo > 0) { + console.log("SETTING LAST LOGIN: " + loginInfo) + lastLogin = new Date(loginInfo * 1000).toISOString().slice(0, 10) + " (" + data.login_info.length + ")" + } + } + return ( ) : null} - + { setSelectedUserModalOpen(true); @@ -3300,6 +3338,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user New apikey */} + + ); })} diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 00cd2c16..6f44a3b2 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -977,14 +977,17 @@ const AngularWorkflow = (defaultprops) => { tmpView = execution_id; } + console.log("TMPVIEW: ", tmpView); + // Compare with currently selected item if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { // Don't clean up if it's already open if (executionModalOpen === true) { + console.log("Execution modal already open, not cleaning up") return } - const execution = responseJson.find((data) => data.execution_id === tmpView); + const execution = responseJson.executions.find((data) => data.execution_id === tmpView); setExecutionModalOpen(true) if (execution !== null && execution !== undefined) { diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index 41443966..f961076b 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -170,9 +170,6 @@ const UsecaseListComponent = (props) => { //console.log("parseUsecase: ", subcase) const srcdata = findSpecificApp(frameworkData, subcase.type) const dstdata = findSpecificApp(frameworkData, subcase.last) - - //console.log("srcdata: ", srcdata) - //console.log("dstdata: ", dstdata) if (srcdata !== undefined && srcdata !== null) { subcase.srcimg = srcdata.large_image @@ -192,9 +189,9 @@ const UsecaseListComponent = (props) => { subcase = parseUsecase(subcase) // Timeout 50ms to delay it slightly - setTimeout(() => { - setInputUsecase(subcase) - }, 50) + //setTimeout(() => { + // setInputUsecase(subcase) + //}, 50) fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, { method: "GET", @@ -212,12 +209,13 @@ const UsecaseListComponent = (props) => { return response.json(); }) .then((responseJson) => { - //console.log("In responseJson for usecase: ", responseJson) var parsedUsecase = responseJson if (responseJson.success === false) { parsedUsecase = subcase } else { + console.log("FOUND: ", JSON.parse(JSON.stringify(responseJson))) + parsedUsecase = responseJson parsedUsecase.srcimg = subcase.srcimg @@ -417,7 +415,7 @@ const UsecaseListComponent = (props) => { const fixedName = subcase.name.toLowerCase().replace("_", " ") return ( - { + { //setSelectedWorkflows([]) if (selectedItem) { @@ -887,21 +885,23 @@ const UsecaseListComponent = (props) => { > Try it out: - 0 ? + + apps={apps} + /> + : null} {/* diff --git a/frontend/src/views/Search.jsx b/frontend/src/views/Search.jsx index ffa2061f..1f696aad 100644 --- a/frontend/src/views/Search.jsx +++ b/frontend/src/views/Search.jsx @@ -21,20 +21,13 @@ import { } from "@mui/icons-material"; -const bodyDivStyle = { - margin: "auto", - maxWidth: 1024, - scrollX: "hidden", - overflowX: "hidden", -} - // Should be different if logged in :| const Search = (props) => { - const { globalUrl, isLoaded, serverside, userdata, hidemargins, } = props; + const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader} = props; let navigate = useNavigate(); const [curTab, setCurTab] = useState(0); - const iconStyle = { marginRight: 10 }; + const iconStyle = { marginRight: isHeader ? null : 10 }; useEffect(() => { if (serverside !== true && window.location.search !== undefined && window.location.search !== null) { @@ -58,15 +51,23 @@ const Search = (props) => { return null } + const bodyDivStyle = { + margin: "auto", + maxWidth: 1024, + scrollX: "hidden", + overflowX: "hidden", + justifyContent: isHeader ? "center" : null, + } + const boxStyle = { color: "white", flex: "1", - marginLeft: 10, - marginRight: 10, - paddingLeft: 30, - paddingRight: 30, - paddingBottom: 30, - paddingTop: hidemargins === true ? 0 : 30, + marginLeft: isHeader ? null : 10, + marginRight: isHeader ? null : 10, + paddingLeft: isHeader ? null : 30, + paddingRight: isHeader ? null : 30, + paddingBottom: isHeader ? null : 30, + paddingTop: hidemargins === true ? 0 : isHeader ? null : 30, display: "flex", flexDirection: "column", overflowX: "hidden", @@ -121,7 +122,7 @@ const Search = (props) => {
{ /> {curTab === 0 ? - + : curTab === 1 ? window.location.pathname === "/search" ? @@ -164,7 +165,7 @@ const Search = (props) => { : curTab === 3 ? - + : null}
diff --git a/frontend/src/views/Welcome.jsx b/frontend/src/views/Welcome.jsx index f9cab314..1b2085f4 100644 --- a/frontend/src/views/Welcome.jsx +++ b/frontend/src/views/Welcome.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import ReactGA from 'react-ga4'; import WelcomeForm2 from "../components/WelcomeForm2.jsx"; import AppFramework from "../components/AppFramework.jsx"; - +import {isMobile} from "react-device-detect"; import { ArrorForwardIos as ArrowForwardIosIcon, } from '@mui/icons-material'; @@ -269,22 +269,22 @@ const Welcome = (props) => { getApps() getAvailableWorkflows() - if ( - window.location.search !== undefined && - window.location.search !== null - ) { + if (window.location.search !== undefined && window.location.search !== null) { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); const foundTab = params["tab"]; + + // Make foundtab into a number + console.log("foundTab: ", foundTab, activeStep+1) if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab) && foundTab >= 1 && foundTab <= 3) { + console.log("SETTING TAB TO: ", foundTab) + setShowWelcome(true) if (foundTab === 3 || foundTab === "3") { handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase) } setActiveStep(foundTab-1) - } else { - navigate(`/welcome?tab=2`) } } }, []) @@ -297,8 +297,8 @@ const Welcome = (props) => { flex: 1, padding: 0, textAlign: "center", - maxWidth: 275, - minWidth: 275, + maxWidth: isMobile ? null : 275, + minWidth: isMobile ? null : 275, backgroundColor: theme.palette.surfaceColor, color: "white", borderRadius: theme.palette.borderRadius, @@ -323,7 +323,7 @@ const Welcome = (props) => { const buttonStyle = { borderRadius: 200, height: 51, - width: 464, + width: isMobile ? 300 : 464, fontSize: 16, // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", @@ -331,7 +331,7 @@ const Welcome = (props) => { top: 105, margin: "auto", itemAlign: "center", - marginLeft: "65px", + marginLeft: isMobile? null : "65px", } const defaultImage = "/images/experienced.png" @@ -379,7 +379,7 @@ const Welcome = (props) => { })}
*/} - +
{/* @@ -391,6 +391,7 @@ const Welcome = (props) => { /> */} { />
- {/* {frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null : -
- - App Framework - - - - -
- } */}
: -
- - {/*
- { - navigate("/workflows") - }}/> - { - navigate("/workflows") - }}> - Back - -
*/} - +
Help us get to know you @@ -466,7 +429,7 @@ const Welcome = (props) => { {/* We will use this information to personalize your automation */} -
+
{ if (isCloud) { @@ -479,6 +442,7 @@ const Welcome = (props) => { //setActiveStep(1) } + navigate("/welcome?tab=2") setShowWelcome(true) }}> @@ -487,12 +451,13 @@ const Welcome = (props) => { New to Shuffle - Let us guide you for an easier experience + Let us guide you for an easier experience
-
+
{/* OR */} @@ -521,10 +486,11 @@ const Welcome = (props) => {
-
+
- - - - {/*
{ - if (window.drift !== undefined) { - window.drift.api.startInteraction({ interactionId: 340045 }) - } else { - console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) - } - }}> - - Want a demo instead? - -
*/}
} diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 08da0bd1..5be518de 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -1410,6 +1410,7 @@ func main() { continue } + //defer newresp.Body.Close() if newresp.StatusCode == 405 { log.Printf("[WARNING] Received 405 from %s. This is likely due to a misconfigured base URL. Automatically swapping to GET request (backwards compatibility)", fullUrl) @@ -1619,6 +1620,7 @@ func main() { continue } + defer resultResp.Body.Close() body, err := ioutil.ReadAll(resultResp.Body) if err != nil { log.Printf("[ERROR] Failed reading confirm body: %s", err) @@ -1913,6 +1915,7 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error { return err } + defer newresp.Body.Close() body, err := ioutil.ReadAll(newresp.Body) if err != nil { log.Printf("[ERROR] Failed reading body in worker request body to worker on %s: %s", streamUrl, err) From 96b570c859770ffd189713dd86c86854a49cdbcf Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 8 Nov 2023 19:28:05 +0100 Subject: [PATCH 3/5] Minor admin fixes --- frontend/src/components/Billing.jsx | 8 ++++---- frontend/src/views/Admin.jsx | 4 ++-- frontend/src/views/Apps.jsx | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 6f493062..f20d5b1e 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -327,9 +327,9 @@ const Billing = (props) => { style: { pointerEvents: "auto", color: "white", - minWidth: 650, + minWidth: 750, padding: 30, - maxHeight: 600, + maxHeight: 700, overflowY: "auto", overflowX: "hidden", zIndex: 10012, @@ -353,10 +353,10 @@ const Billing = (props) => { - Sign EULA + Read and Accept the EULA selected.join(', ')} MenuProps={MenuProps} > - {["contacted", "lead", "pov", "demo done", "customer", "open source", "student", "internal", "old customer", "creator", "tech partner"].map((name) => ( + {["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "old customer", "creator", "tech partner"].map((name) => ( -1} /> @@ -2544,7 +2544,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user /> - Licensing (Beta) + Licensing /> { refine(event.currentTarget.value) }} - limit={5} /> {/*isSearchStalled ? 'My search is stalled' : ''*/} From 7fe01b0e0a980d5bd99df199c18b0a25cf0918f5 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 8 Nov 2023 20:32:28 +0100 Subject: [PATCH 4/5] Added SHUFFLE_APP_REPLICAS environment var --- functions/onprem/orborus/orborus.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 5be518de..0cd4f2cb 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -406,6 +406,17 @@ func deployServiceWorkers(image string) { cnt = 1 } + appReplicas := os.Getenv("SHUFFLE_APP_REPLICAS") + appReplicaCnt := 1 + if len(appReplicas) > 0 { + newCnt, err := strconv.Atoi(appReplicas) + if err != nil { + log.Printf("[ERROR] %s is not a valid number for SHUFFLE_APP_REPLICAS", appReplicas) + } else { + appReplicaCnt = newCnt + } + } + log.Printf("[DEBUG] Found %d node(s) to replicate over. Defaulting to 1 IF we can't auto-discover them.", cnt) replicatedJobs := uint64(replicas * nodeCount) @@ -462,7 +473,7 @@ func deployServiceWorkers(image string) { Env: []string{ fmt.Sprintf("SHUFFLE_SWARM_CONFIG=%s", os.Getenv("SHUFFLE_SWARM_CONFIG")), fmt.Sprintf("SHUFFLE_SWARM_NETWORK_NAME=%s", networkName), - fmt.Sprintf("SHUFFLE_APP_REPLICAS=%d", cnt), + fmt.Sprintf("SHUFFLE_APP_REPLICAS=%d", appReplicaCnt), fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")), fmt.Sprintf("SHUFFLE_DEBUG_MEMORY=%s", os.Getenv("SHUFFLE_DEBUG_MEMORY")), fmt.Sprintf("TZ=%s", timezone), From a3af4c09ae05883e006e5d8227d78ebe1e083166 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 9 Nov 2023 09:05:27 +0100 Subject: [PATCH 5/5] Split up nginx v1 and v2 again to fix workflow run loading --- frontend/confd/templates/nginx.conf | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/confd/templates/nginx.conf b/frontend/confd/templates/nginx.conf index 6d1d6081..22b98574 100755 --- a/frontend/confd/templates/nginx.conf +++ b/frontend/confd/templates/nginx.conf @@ -70,7 +70,18 @@ http { try_files $uri /index.html; } - location ~ /api/v(1|2) { + location /api/v1 { + proxy_pass http://{{ getenv "BACKEND_HOSTNAME" "shuffle-backend" }}:5001; + proxy_buffering off; + proxy_http_version 1.1; + + proxy_connect_timeout 900; + proxy_send_timeout 900; + proxy_read_timeout 900; + send_timeout 900; + } + + location /api/v2 { proxy_pass http://{{ getenv "BACKEND_HOSTNAME" "shuffle-backend" }}:5001; proxy_buffering off; proxy_http_version 1.1;