- Your Self-Hosted Orborus instance
+ Self-Hosted Orborus instance
Orborus is the Shuffle queue handler that runs your hybrid workflows and manages pipelines. It can be run in Docker/k8s container on your server or in your cluster. Follow the steps below, and configure as need be.
@@ -1399,9 +1399,23 @@ const EnvironmentTab = memo((props) => {
>
{getOrborusCommand(environment)}
-
+
+
+ {
+ navigator.clipboard.writeText(orborusCommandWrapper())
+ toast("Copied to clipboard")
+ }}
+ >
+
+
+
diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx
index 8ee1f1be..e942db9b 100644
--- a/frontend/src/components/LeftSideBar.jsx
+++ b/frontend/src/components/LeftSideBar.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useState, useContext, useCallback, useMemo } from "react";
+import React, { useEffect, useRef, useState, useContext, useCallback, useMemo, memo } from "react";
import {
ExpandLess as ExpandLessIcon,
ExpandMore as ExpandMoreIcon,
@@ -754,35 +754,6 @@ useEffect(() => {
fontSize: 18
};
- const modalView = (
-
{
- setSearchBarModalOpen(false);
- }}
- PaperProps={{
- style: {
- color: "white",
- minWidth: 750,
- height: 785,
- borderRadius: 16,
- border: "1px solid var(--Container-Stroke, #494949)",
- background: "var(--Container, #000000)",
- boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
- zIndex: 13000,
- paddingTop: 20,
- },
- }}
- >
-
-
-
-
-
-
-
- );
-
const getRegionFlag = (region_url) => {
var region = "gb";
const regionMapping = {
@@ -821,7 +792,7 @@ useEffect(() => {
height: "calc((100vh - 32px)*1.2)",
}}
>
- {modalView}
+ {searchBarModalOpen ?
: null}
{
};
export default LeftSideBar;
+
+
+const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata}) => {
+ return (
+ (
+ {
+ setSearchBarModalOpen(false);
+ }}
+ PaperProps={{
+ style: {
+ color: "white",
+ minWidth: 750,
+ height: 785,
+ borderRadius: 16,
+ border: "1px solid var(--Container-Stroke, #494949)",
+ background: "var(--Container, #000000)",
+ boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
+ zIndex: 13000,
+ paddingTop: 20,
+ },
+ }}
+ >
+
+
+
+
+
+
+
+ )
+ )
+});
diff --git a/frontend/src/components/OrgHeaderexpandedNew.jsx b/frontend/src/components/OrgHeaderexpandedNew.jsx
index e7ba725f..403d1bc4 100644
--- a/frontend/src/components/OrgHeaderexpandedNew.jsx
+++ b/frontend/src/components/OrgHeaderexpandedNew.jsx
@@ -310,11 +310,13 @@ const [orgName, setOrgName] = useState(selectedOrganization?.name);
if(region === "US") {
region = "us-west2"
} else if(region === "EU") {
- region = "europe-west3"
+ region = "europe-west2"
} else if(region === "CA") {
region = "northamerica-northeast1"
} else if(region === "UK") {
region = "europe-west2"
+ } else if (region === "EU-2") {
+ region = "europe-west3"
}
var data = {
diff --git a/frontend/src/components/OrganizationTab.jsx b/frontend/src/components/OrganizationTab.jsx
index 01d39f09..05ef7778 100644
--- a/frontend/src/components/OrganizationTab.jsx
+++ b/frontend/src/components/OrganizationTab.jsx
@@ -3,7 +3,7 @@ import { Link, useNavigate, useLocation } from "react-router-dom";
import Billing from "../components/Billing.jsx";
import Priorities from "../components/Priorities.jsx";
import Branding from "../components/Branding.jsx";
-// import AnalyticsTab from '../components/AnalyticsTab.jsx';
+import AnalyticsTab from '../components/AnalyticsTab.jsx';
import EditOrgTab from '../components/EditOrgTab.jsx';
import CloudSyncTab from '../components/CloudSyncTab.jsx';
import SSOTab from "../components/ssoTab.jsx"
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx
index 49fd20a8..b8e345d9 100755
--- a/frontend/src/views/AngularWorkflow.jsx
+++ b/frontend/src/views/AngularWorkflow.jsx
@@ -4122,7 +4122,10 @@ const AngularWorkflow = (defaultprops) => {
setCookie("session_token", sessionToken, { path: "/" });
window.location.href = "https://shuffler.io/workflows/3abdfb21-b40f-4e50-b855-ac0d62f83cbe";
}, 2000)
- }
+ } else if (execFound !== null && response.status >= 300) {
+ toast.info("Failed to load the workflow, but you may still find a list of workflow runs if you have access.")
+ setExecutionModalOpen(true)
+ }
}
}
diff --git a/frontend/src/views/AppExplorer.jsx b/frontend/src/views/AppExplorer.jsx
index 3fec4d41..03c6ca6f 100644
--- a/frontend/src/views/AppExplorer.jsx
+++ b/frontend/src/views/AppExplorer.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useContext, useRef } from "react";
+import React, { useState, useEffect, useContext } from "react";
import theme from "../theme.jsx";
import ReactGA from "react-ga4";
@@ -223,7 +223,6 @@ const AppExplorer = (props) => {
result: baseResult,
});
const [executing, setExecuting] = useState(false);
- const [activatedApps, setActivatedApps] = useState([]);
const [anchorEl, setAnchorEl] = React.useState(null);
const [creatorProfile, setCreatorProfile] = React.useState({});
const [selectedTab, setSelectedTab] = React.useState(0);
@@ -248,7 +247,6 @@ const AppExplorer = (props) => {
})
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
- const isFirstRequestSentRef = useRef(false);
// FIXME: This is used, as useEffect() creates an issue with apps not loading at all
@@ -301,15 +299,13 @@ const AppExplorer = (props) => {
if (serverside) {
console.log("Not getting app because serverside.");
} else {
- if (isCloud) {
- if (params.appid.length === 32 || params.appid.length === 36) {
- handleEditApp(params.appid);
- runAlgoliaAppSearch(params.appid, false, true);
- } else {
- runAlgoliaAppSearch(params.appid);
-
- //handleEditApp()
- }
+ if (params.appid.length === 32 || params.appid.length === 36) {
+ handleEditApp(params.appid);
+ runAlgoliaAppSearch(params.appid, false, true);
+ } else {
+ runAlgoliaAppSearch(params.appid);
+
+ //handleEditApp()
}
}
//parseIncomingOpenapiData(YAML.parse(data))
@@ -348,86 +344,6 @@ const AppExplorer = (props) => {
loadOrganization(userdata.active_org.id)
}
- const getApps = () => {
- // Get apps from localstorage
- var storageApps = []
- try {
- const appstorage = localStorage.getItem("apps")
- storageApps = JSON.parse(appstorage)
- if (storageApps === null || storageApps === undefined || storageApps.length === 0) {
- storageApps = []
- } else {
- setActivatedApps(storageApps)
- }
- } catch (e) {
- //console.log("Failed to get apps from localstorage: ", e)
- }
-
-
- fetch(globalUrl + "/api/v1/apps", {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- })
- .then((response) => {
- if (response.status !== 200) {
- console.log("Status not 200 for apps :O!");
-
- //if (isCloud) {
- // window.location.pathname = "/search";
- //}
- }
-
- return response.json();
- })
- .then((responseJson) => {
- var privateapps = [];
- var valid = [];
- var invalid = [];
- for (var key in responseJson) {
- const app = responseJson[key];
-
- if (app.categories !== undefined && app.categories !== null && app?.categories.includes("Eradication")) {
- app.categories = ["EDR"]
- }
-
- if (app.is_valid && !(!app.activated && app.generated)) {
- privateapps.push(app);
- } else if (
- app.private_id !== undefined &&
- app.private_id.length > 0
- ) {
- valid.push(app);
- } else {
- invalid.push(app);
- }
- }
-
- privateapps.push(...valid);
- privateapps.push(...invalid);
- setActivatedApps(privateapps);
-
- if (params.appid.length === 32 || params.appid.length === 36) {
- handleEditApp(params.appid, privateapps);
- runAlgoliaAppSearch(params.appid, false, true, privateapps);
- } else {
- runAlgoliaAppSearch(params.appid,true, false, privateapps);
- }
- })
- .catch((error) => {
- toast(error.toString());
- });
- };
-
- useEffect(() => {
- if (activatedApps?.length === 0 && !isCloud) {
- getApps();
- }
- }, [activatedApps]);
-
var activateButton = (
{
});
};
-
-
- const HandleActivateApp = (appid) => {
- if (isFirstRequestSentRef.current) {
- console.log("Skipping duplicate activation request for app:", appid);
- return;
- }
-
- isFirstRequestSentRef.current = true;
-
- toast.info("App is not activated. Activating it now. Please wait...");
-
- const baseURL = globalUrl;
- const url = `${baseURL}/api/v1/apps/${appid}/activate`;
-
- fetch(url, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- })
- .then((response) => response.json())
- .then((responseJson) => {
- if (!responseJson.success) {
- toast.error("Failed to activate app. Redirecting to https://shuffler.io. Please wait...");
- setTimeout(() => {
- window.location.replace(`https://shuffler.io/apps/${appid}`);
- }, 2000);
- return;
- }
-
- localStorage.removeItem("apps");
- getApps();
- })
- .catch((error) => {
- console.log("App activation error: ", error.toString());
- toast.error("Failed to activate app. Redirecting to https://shuffler.io. Please wait...");
- setTimeout(() => {
- window.location.href = `https://shuffler.io/apps/${appid}`;
- }, 2000);
- });
- };
-
-
-
-
- const handleEditApp = (appid, privateapps) => {
+ const handleEditApp = (appid) => {
if (serverside === true) {
return;
}
- var newAppId = appid
- var appIdFound = false
-
- if (!isCloud) {
- // Ensure privateapps is always an array
- privateapps = Array.isArray(privateapps) ? privateapps : [];
-
- let found = privateapps.find(app => {
- return app.id?.trim().toLowerCase() === appid
- });
-
- if (!found) {
- found = privateapps.find(app => {
- return app.published_id?.trim().toLowerCase() === appid;
- });
- }
-
-
- if (found && found.id) {
- newAppId = found.id;
- appIdFound = true
- } else {
- if (!isFirstRequestSentRef.current) {
- HandleActivateApp(appid);
- return;
- } else {
- toast.info("App activated but can't load it for now. Loading app on https://shuffler.io. Please wait...");
- setTimeout(() => {
- window.location.href = `https://shuffler.io/apps/${appid}`;
- }, 3000);
- }
- }
-
- }
+ setAppId(appid)
- setAppId(newAppId)
-
- if (isFirstRequestSentRef.current && !isCloud && !appIdFound) {
- return
- }
-
- fetch(`${globalUrl}/api/v1/apps/${newAppId}/config`, {
+ fetch(globalUrl + "/api/v1/apps/" + appid + "/config", {
method: "GET",
headers: {
"Content-Type": "application/json",
@@ -997,9 +826,9 @@ const AppExplorer = (props) => {
) {
toast("Failed to get the app")
setIsAppLoaded(true)
- // setTimeout(() => {
- // navigate("/search")
- // }, 1000);
+ setTimeout(() => {
+ navigate("/search")
+ }, 1000);
return;
} else {
parseIncomingOpenapiData(responseJson);
@@ -1010,7 +839,6 @@ const AppExplorer = (props) => {
});
};
-
const parseIncomingAppdata = (data, openapiExists) => {
document.title = data.name + " App - OpenAPI and API";
@@ -1594,7 +1422,7 @@ const AppExplorer = (props) => {
});
};
- const runAlgoliaAppSearch = (appname, isOriginal, triggerOnly, privatedapps) => {
+ const runAlgoliaAppSearch = (appname, isOriginal, triggerOnly) => {
const index = searchClient.initIndex("appsearch");
console.log("Running appsearch for: ", appname);
@@ -1609,7 +1437,7 @@ const AppExplorer = (props) => {
if (hits !== undefined && hits !== null && hits.length === 1) {
found = true
if (isOriginal !== false) {
- handleEditApp(hits[0].objectID, privatedapps)
+ handleEditApp(hits[0].objectID)
} else {
setSecondaryApp(hits[0])
}
@@ -1644,7 +1472,7 @@ const AppExplorer = (props) => {
} else {
if (isOriginal !== false) {
found = true
- handleEditApp(hit.objectID, privatedapps);
+ handleEditApp(hit.objectID);
} else {
console.log("Found second app: ", hit);
hit.name = hit.name.charAt(0).toUpperCase() + hit.name.slice(1);
@@ -1659,7 +1487,7 @@ const AppExplorer = (props) => {
if (!found) {
if (hits.length > 0) {
if (isOriginal !== false) {
- handleEditApp(hits[0].objectID, privatedapps)
+ handleEditApp(hits[0].objectID)
} else {
setSecondaryApp(hits[0]);
}
@@ -1717,13 +1545,11 @@ const AppExplorer = (props) => {
setTriggers([])
//handleEditApp(params.appid)
- if (isCloud) {
- if (params.appid.length === 32 || params.appid.length === 36) {
- handleEditApp(params.appid);
- runAlgoliaAppSearch(params.appid, false, true);
- } else {
- runAlgoliaAppSearch(params.appid);
- }
+ if (params.appid.length === 32 || params.appid.length === 36) {
+ handleEditApp(params.appid);
+ runAlgoliaAppSearch(params.appid, false, true);
+ } else {
+ runAlgoliaAppSearch(params.appid);
}
}
@@ -3216,7 +3042,9 @@ const AppExplorer = (props) => {
const getDownloadUrl = () => {
- return `curl -X POST ${globalUrl}/api/v1/download_docker_image \\\n -H \"Authorization: Bearer ${userdata?.apikey}" \\\n -d '{"name": "frikky/shuffle:${app?.name.toLowerCase().replaceAll(' ', '_')}_${app.app_version}"}' \\\n -o image.zip; \\\n docker load -i image.zip`
+ const appEnding = app?.public === true ? app?.app_verison : app?.id
+
+ return `curl -L \ \\\n "${globalUrl}/api/v1/download_docker_image?image=frikky/shuffle:${app?.name.toLowerCase().replaceAll(' ', '_')}_${appEnding}" \\\n -H \"Authorization: Bearer ${userdata?.apikey}" \\\n -o image.zip; \\\n docker load -i image.zip`
}
const renderedActionOptions = deduplicateByName((
@@ -3406,9 +3234,11 @@ const AppExplorer = (props) => {
Use the App onprem
- Due to using docker containers with private containers, we had to use a custom registry. Use the command below to download the image to the server if it fails to run.
+ Due to using docker containers with privately uploaded containers, we had to use a custom registry. Use the command below to download the image to the server if it fails to run.
- PS: This does NOT work for ARM containers.
+ It will authenticate and authorize you, before redirecting to a Signed URL on https://storage.googleapis.com
+
+ Now also works for ARM containers!
{
) : null;
- console.log("App: ", app);
-
- console.log("userdata is: ", userdata);
-
const landingpageDataBrowser = (
{
{isMobile ? null : (
{
@@ -4263,7 +4089,7 @@ const AppExplorer = (props) => {
}}
style={{ height: 40, marginTop: 5 }}
>
- {((userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) )|| !isCloud ) ?
+ {userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) ?
"Deactivate App"
:
"Activate App"