Merge branch 'main' into main

This commit is contained in:
Monil Prajapati
2025-03-04 01:03:03 +05:30
committed by GitHub
8 changed files with 180 additions and 69 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ module shuffle
go 1.22.2 go 1.22.2
//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
require ( require (
cloud.google.com/go/datastore v1.15.0 cloud.google.com/go/datastore v1.15.0
+41 -1
View File
@@ -34,7 +34,7 @@ import RunWorkflow from "./views/RunWorkflow.jsx";
import Admin2 from "./views/Admin2.jsx"; import Admin2 from "./views/Admin2.jsx";
import LoginPage from "./views/LoginPage.jsx"; import LoginPage from "./views/LoginPage.jsx";
//import LoginPage from "./views/LoginPage.jsx"; import LoginPageOld from "./views/LoginPageOld.jsx";
import SettingsPage from "./views/SettingsPage.jsx"; import SettingsPage from "./views/SettingsPage.jsx";
import KeepAlive from "./views/KeepAlive.jsx"; import KeepAlive from "./views/KeepAlive.jsx";
@@ -271,6 +271,44 @@ const App = (message, props) => {
} }
/> />
<Route
exact
path="/login2"
element={
<LoginPageOld
isLoggedIn={isLoggedIn}
setIsLoggedIn={setIsLoggedIn}
register={false}
inregister={false}
isLoaded={isLoaded}
globalUrl={globalUrl}
setCookie={setCookie}
cookies={cookies}
checkLogin={checkLogin}
{...props}
/>
}
/>
<Route
exact
path="/loginsetup"
element={
<LoginPageOld
isLoggedIn={isLoggedIn}
setIsLoggedIn={setIsLoggedIn}
register={false}
inregister={false}
isLoaded={isLoaded}
globalUrl={globalUrl}
setCookie={setCookie}
cookies={cookies}
checkLogin={checkLogin}
{...props}
/>
}
/>
<Route <Route
exact exact
path="/register" path="/register"
@@ -390,6 +428,7 @@ const App = (message, props) => {
} }
/> />
) : null} ) : null}
<Route <Route
exact exact
path="/AdminSetup" path="/AdminSetup"
@@ -402,6 +441,7 @@ const App = (message, props) => {
/> />
} }
/> />
<Route <Route
exact exact
path="/detectionframework" path="/detectionframework"
+26 -15
View File
@@ -87,21 +87,21 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
return orgOptions.find((option) => option.name === selectedOrg); return orgOptions.find((option) => option.name === selectedOrg);
}, [selectedOrg, orgOptions]); }, [selectedOrg, orgOptions]);
//With this code it is opening search bar on google chrome search bar as well which is not required //With this code it is opening search bar on google chrome search bar as well which is not required
useEffect(() => { useEffect(() => {
const handleKeyDown = (event) => { const handleKeyDown = (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === "k") { if ((event.ctrlKey || event.metaKey) && event.key === "k") {
event.preventDefault(); event.preventDefault();
setSearchBarModalOpen((prev)=> !prev); setSearchBarModalOpen((prev)=> !prev);
} }
}; };
window.addEventListener("keydown", handleKeyDown); window.addEventListener("keydown", handleKeyDown);
return () => { return () => {
window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keydown", handleKeyDown);
}; };
}, [setSearchBarModalOpen]); }, [setSearchBarModalOpen]);
const CustomPopper = (props) => { const CustomPopper = (props) => {
return ( return (
@@ -155,6 +155,7 @@ useEffect(() => {
> >
{props.children} {props.children}
</Box> </Box>
<Link to="/admin?tab=tenants" style={hrefStyle}> <Link to="/admin?tab=tenants" style={hrefStyle}>
<Box <Box
sx={{ sx={{
@@ -1605,6 +1606,18 @@ useEffect(() => {
marginLeft: 5, marginLeft: 5,
}} }}
> >
{expandLeftNav &&
<Button
variant="outlined"
style={{marginBottom: 15, borderWidth: 2, }}
onClick={() => {
window.open("https://shuffler.io/contact?category=book_a_demo", "_blank")
}}
>
Book a Demo
</Button>
}
<Box ref={autocompleteRef}> <Box ref={autocompleteRef}>
<Autocomplete <Autocomplete
disablePortal disablePortal
@@ -1898,8 +1911,6 @@ useEffect(() => {
}; };
export default LeftSideBar; export default LeftSideBar;
const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata}) => { const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata}) => {
return ( return (
( (
+1 -1
View File
@@ -785,7 +785,7 @@ const TenantsTab = memo((props) => {
<div style={{ marginBottom: 20 }}> <div style={{ marginBottom: 20 }}>
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Tenants</h2> <h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Tenants</h2>
<span style={{ color: textColor }}> <span style={{ color: textColor }}>
Control sub organizations (tenants)! {" "} Create, manage and change to sub-organizations (tenants)! {" "}
{isCloud {isCloud
? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out." ? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out."
: ''} : ''}
+18 -10
View File
@@ -1,6 +1,8 @@
/* eslint-disable react/no-multi-comp */ /* eslint-disable react/no-multi-comp */
import React, { useState } from "react"; import React, { useState } from "react";
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";
import theme from '../theme.jsx';
import { useNavigate } from "react-router-dom";
import { import {
CircularProgress, CircularProgress,
@@ -20,11 +22,8 @@ const surfaceColor = "#27292D";
const inputColor = "#383B40"; const inputColor = "#383B40";
const boxStyle = { const boxStyle = {
paddingLeft: "30px", padding: 40,
paddingRight: "30px", backgroundColor: theme.palette.backgroundColor,
paddingBottom: "30px",
paddingTop: "30px",
backgroundColor: surfaceColor,
}; };
const useStyles = makeStyles({ const useStyles = makeStyles({
@@ -41,8 +40,10 @@ const AdminAccount = (props) => {
const [firstRequest, setFirstRequest] = useState(true); const [firstRequest, setFirstRequest] = useState(true);
const [loginLoading, setLoginLoading] = useState(false); const [loginLoading, setLoginLoading] = useState(false);
// Used to swap from login to register. True = login, false = register // Used to swap from login to register. True = login, false = register
const register = true; const register = true;
let navigate = useNavigate();
const classes = useStyles(); const classes = useStyles();
// Error messages etc // Error messages etc
@@ -70,13 +71,16 @@ const AdminAccount = (props) => {
setLoginInfo(responseJson["reason"]); setLoginInfo(responseJson["reason"]);
} else { } else {
if (responseJson.reason === "redirect") { if (responseJson.reason === "redirect") {
window.location.pathname = "/login"; setTimeout(() => {
window.location.pathname = "/login";
}, 2500)
} }
} }
}) })
) )
.catch((error) => { .catch((error) => {
setLoginInfo("Error in userdata: ", error); setLoginInfo("Error in userdata (1): ", error);
navigate("/loginsetup")
}); });
}; };
@@ -108,13 +112,17 @@ const AdminAccount = (props) => {
setLoginInfo(responseJson["reason"]); setLoginInfo(responseJson["reason"]);
} else { } else {
setLoginInfo("Successful register :)"); setLoginInfo("Successful register :)");
window.location.pathname = "/login";
setTimeout(() => {
window.location.pathname = "/login";
}, 2500)
} }
}) })
) )
.catch((error) => { .catch((error) => {
setLoginLoading(false); setLoginInfo("Error in userdata (2): ", error);
setLoginInfo("Error in userdata: ", error); setLoginLoading(false)
navigate("/loginsetup")
}); });
}; };
+30 -23
View File
@@ -2540,18 +2540,21 @@ const AngularWorkflow = (defaultprops) => {
} }
*/ */
// FIXME: What is this?
if (cy !== undefined && cy !== null) { if (cy !== undefined && cy !== null) {
// scale: 0.3, // scale: 0.3,
// bg: "#27292d", // bg: "#27292d",
const cyImageData = cy.png({ if (cy.png !== undefined && cy.png !== null) {
output: "base64uri", const cyImageData = cy.png({
maxWidth: 480, output: "base64uri",
maxHeight: 270, maxWidth: 480,
}) maxHeight: 270,
})
if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) { if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) {
useworkflow.image = cyImageData useworkflow.image = cyImageData
} }
}
} }
if (useworkflow.id === undefined || useworkflow.id === null || useworkflow.id.length === 0) { if (useworkflow.id === undefined || useworkflow.id === null || useworkflow.id.length === 0) {
@@ -9785,9 +9788,14 @@ const AngularWorkflow = (defaultprops) => {
toast("Successfully stopped schedule"); toast("Successfully stopped schedule");
} }
workflow.triggers[triggerindex].status = "stopped"; if (triggerindex !== undefined && triggerindex !== null && triggerindex >= 0) {
trigger.status = "stopped"; workflow.triggers[triggerindex].status = "stopped";
setSelectedTrigger(trigger); }
//trigger.status = "stopped";
//console.log("TRIGGER: ", trigger)
//setSelectedTrigger(trigger);
setWorkflow(workflow); setWorkflow(workflow);
saveWorkflow(workflow) saveWorkflow(workflow)
@@ -14637,20 +14645,19 @@ const AngularWorkflow = (defaultprops) => {
cy.add(cybranch); cy.add(cybranch);
} }
console.log("Value to be set: ", e.target.value);
try { try {
workflow.triggers[ workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id
selectedTriggerIndex } catch(e) {
].parameters[3].value = e.target.value.id; console.log("Error: ", e)
} catch { workflow.triggers[selectedTriggerIndex].parameters[3] =
workflow.triggers[selectedTriggerIndex].parameters[3] = {
{ name: "startnode",
name: "startnode", value: e.target.value.id,
value: e.target.value.id, };
};
} }
setWorkflow(workflow); setWorkflow(workflow)
setUpdate(Math.random())
} }
} }
@@ -15035,7 +15042,7 @@ const AngularWorkflow = (defaultprops) => {
<Tooltip arrow placement="left" title={ <Tooltip arrow placement="left" title={
<span style={{}}> <span style={{}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} /> <img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, maxWidth: 285, borderRadius: theme.palette?.borderRadius, }} />
: null} : null}
<Typography> <Typography>
Choose Subflow '{data.name}' Choose Subflow '{data.name}'
+56 -13
View File
@@ -1,9 +1,10 @@
/* eslint-disable react/no-multi-comp */ /* eslint-disable react/no-multi-comp */
import React, { useState } from 'react'; import React, { useState, useEffect, } from 'react';
import { makeStyles } from '@mui/styles'; import { makeStyles } from '@mui/styles';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { useInterval } from "react-powerhooks";
import { import {
@@ -259,7 +260,7 @@ const MarketplaceCard = ({ classes }) => {
target="_blank" target="_blank"
style={{ color: "rgba(255, 132, 68, 1)", textDecoration: "underline" }} style={{ color: "rgba(255, 132, 68, 1)", textDecoration: "underline" }}
> >
Read more about self hosting Learn more about self hosting
</a> </a>
</div > </div >
@@ -269,7 +270,7 @@ const MarketplaceCard = ({ classes }) => {
const LoginPage = props => { const LoginPage = props => {
const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, inregister, serverside } = props; const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, inregister, serverside, checkLogin, } = props;
let navigate = useNavigate(); let navigate = useNavigate();
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
@@ -284,6 +285,21 @@ const LoginPage = props => {
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io";
const parsedsearch = serverside === true ? "" : window.location.search const parsedsearch = serverside === true ? "" : window.location.search
useEffect(() => {
if (!isCloud) {
checkAdmin()
}
}, [])
const { start, stop } = useInterval({
duration: 3000,
startImmediate: false,
callback: () => {
checkAdmin()
},
})
if (serverside !== true) { if (serverside !== true) {
const tmpMessage = new URLSearchParams(window.location.search).get("message") const tmpMessage = new URLSearchParams(window.location.search).get("message")
if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) { if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) {
@@ -409,6 +425,38 @@ const LoginPage = props => {
window.location.pathname = "/workflows" window.location.pathname = "/workflows"
} }
const checkAdmin = () => {
const url = globalUrl + "/api/v1/checkusers";
fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo(responseJson["reason"]);
} else {
// Stay = 0 users
// Redirect = >1 user
if (responseJson.reason === "stay") {
setTimeout(() => {
navigate("/adminsetup")
}, 2500)
}
}
})
)
.catch((error) => {
setTimeout(() => {
navigate("/adminsetup")
}, 2500)
})
};
const onSubmit = (e) => { const onSubmit = (e) => {
//toast("Testing from login page") //toast("Testing from login page")
@@ -629,7 +677,7 @@ const LoginPage = props => {
width: "max-content", width: "max-content",
}} }}
> >
<form onSubmit={onSubmit} style={{ margin: 15, width: isMobile ? "100%" : "360px", width: "max-content", overflow: "hidden" }}> <form onSubmit={onSubmit} style={{ margin: 15, width: isMobile ? "100%" : "360px", width: "max-content", overflow: "hidden", textAlign: "center", }}>
<img <img
style={{ style={{
height: isMobile ? 44 : 75, height: isMobile ? 44 : 75,
@@ -667,12 +715,12 @@ const LoginPage = props => {
variant="body2" variant="body2"
> >
{register {register
? "Find new ways to automate by discovering usecases made by Shufflers" ? "Find new ways to automate by discovering usecases Shufflers"
: "Please fill in the information to continue to discover the power of Shuffle" : "Please fill in the information to continue to discover the power of Shuffle"
} }
</Typography> </Typography>
<div style={{ marginBottom: 20 }}> <div style={{ marginBottom: 20, textAlign: "left", }}>
<div style={{ marginBottom: 5 }}>{isCloud ? "Email" : "Username"}</div> <div style={{ marginBottom: 5 }}>{isCloud ? "Email" : "Username"}</div>
<TextField <TextField
color="primary" color="primary"
@@ -704,7 +752,7 @@ const LoginPage = props => {
</div> </div>
{!loginWithSSO && ( {!loginWithSSO && (
<div style={{ marginBottom: 20 }}> <div style={{ marginBottom: 20, textAlign: "left", }}>
<div style={{ marginBottom: 5 }}>Password</div> <div style={{ marginBottom: 5 }}>Password</div>
<TextField <TextField
color="primary" color="primary"
@@ -781,14 +829,9 @@ const LoginPage = props => {
flexDirection: "column", flexDirection: "column",
marginTop: "0px" marginTop: "0px"
}}> }}>
{register && !loginWithSSO && ( {isCloud && register && !loginWithSSO && (
<Link <Link
to={`/passwordreset${parsedsearch}`} to={`/passwordreset${parsedsearch}`}
onChange={() => {
if (!isCloud) {
toast.warn("Talk to your administrator")
}
}}
style={{ style={{
...hrefStyle, ...hrefStyle,
alignSelf: isMobile ? "center" : "flex-end" alignSelf: isMobile ? "center" : "flex-end"
+7 -5
View File
@@ -58,9 +58,9 @@ const LoginDialog = (props) => {
// Used to swap from login to register. True = login, false = register // Used to swap from login to register. True = login, false = register
useEffect(() => { useEffect(() => {
checkAdmin() checkAdmin()
}, [loginViewLoading]) }, [loginViewLoading])
// Error messages etc // Error messages etc
const [loginInfo, setLoginInfo] = useState(""); const [loginInfo, setLoginInfo] = useState("");
@@ -91,6 +91,8 @@ const LoginDialog = (props) => {
if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) {
setSSOUrl(responseJson.sso_url); setSSOUrl(responseJson.sso_url);
} }
navigate("/login")
if (loginViewLoading) { if (loginViewLoading) {
setLoginViewLoading(false); setLoginViewLoading(false);
@@ -125,7 +127,7 @@ const LoginDialog = (props) => {
callback: () => { callback: () => {
checkAdmin(); checkAdmin();
}, },
}); })
if (firstRequest) { if (firstRequest) {
setFirstRequest(false); setFirstRequest(false);
@@ -294,7 +296,7 @@ const LoginDialog = (props) => {
style={{ marginBottom: 20, color: "white" }} style={{ marginBottom: 20, color: "white" }}
> >
Waiting for the Shuffle database to become available. This may Waiting for the Shuffle database to become available. This may
take up to a minute. take up to two minutes.
</Typography> </Typography>
{loginInfo === undefined || {loginInfo === undefined ||