Filesync w 2.0.0-rc3
This commit is contained in:
@@ -100,6 +100,8 @@ const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, se
|
||||
const [ExampleBody, setExampleBody] = useState({});
|
||||
const [filteredActions, setFilteredActions] = useState([]);
|
||||
|
||||
const [firstSendDone, setFirstSendDone] = useState(false)
|
||||
|
||||
const getJsonObject = (properties) => {
|
||||
|
||||
let jsonObject = {};
|
||||
@@ -1425,18 +1427,20 @@ const ActionsList = memo(({
|
||||
};
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const query = e.target.value;
|
||||
setSearchQuery(query);
|
||||
const query = e?.target?.value?.toLowerCase().replaceAll("_", " ");
|
||||
|
||||
setSearchQuery(query)
|
||||
if (query.length === 0) {
|
||||
setVisibleActions(actions);
|
||||
setVisibleActions(actions)
|
||||
} else {
|
||||
setVisibleActions(
|
||||
actions.filter((action) =>
|
||||
action.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
actions?.filter((action) =>
|
||||
action?.name?.toLowerCase()?.replaceAll("_", " ")?.includes(searchQuery)
|
||||
)
|
||||
);
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '350px', width: "25%", overflow: 'hidden', marginLeft: !(isLoggedIn || isLoaded) ? 5 : 0}}>
|
||||
<div style={{ borderBottom: '1px solid #494949', paddingTop: 10, paddingBottom: 10}}>
|
||||
@@ -1465,7 +1469,7 @@ const ActionsList = memo(({
|
||||
</div>
|
||||
<TextField
|
||||
value={searchQuery}
|
||||
placeholder="Search here"
|
||||
placeholder="Search endpoints"
|
||||
onChange={handleSearch}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
@@ -2035,6 +2039,7 @@ const Action = memo((
|
||||
}, [editorRef.current]);
|
||||
|
||||
|
||||
const actionname = action?.name.charAt(0).toUpperCase() + action?.name.slice(1).replaceAll("_", " ")
|
||||
return (
|
||||
<div
|
||||
ref={actionRef}
|
||||
@@ -2042,6 +2047,7 @@ const Action = memo((
|
||||
data-index={index}
|
||||
key={action.name.replace(/ /g, "-").replace(/_/g, "-")}
|
||||
style={{
|
||||
marginTop: 50,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
minHeight: 400,
|
||||
@@ -2066,7 +2072,7 @@ const Action = memo((
|
||||
color: "rgba(241, 241, 241, 1)",
|
||||
}}
|
||||
>
|
||||
{action.name}
|
||||
{actionname}
|
||||
</Typography>
|
||||
<div
|
||||
style={{
|
||||
@@ -2259,6 +2265,13 @@ const Action = memo((
|
||||
}}
|
||||
disabled={disableExecuteButton}
|
||||
onClick={() => {
|
||||
/*
|
||||
if (!firstSendDone) {
|
||||
setFirstSendDone(true)
|
||||
setCurTab(2)
|
||||
}
|
||||
*/
|
||||
|
||||
if (actionUrl.length === 0) {
|
||||
toast.error("URL cannot be empty");
|
||||
return;
|
||||
|
||||
@@ -215,8 +215,8 @@ const Billing = memo((props) => {
|
||||
|
||||
var priceItem = "price_1MROFrDzMUgUjxHShcSxgHO1"
|
||||
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success`
|
||||
const failUrl = `${window.location.origin}/admin?admin_tab=billing&payment=failure`
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
|
||||
const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
|
||||
var checkoutObject = {
|
||||
lineItems: [
|
||||
{
|
||||
@@ -2361,7 +2361,8 @@ const Billing = memo((props) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ marginTop: 40, marginLeft: 10 }}>
|
||||
{isCloud ? (
|
||||
<div style={{ marginTop: 40, marginLeft: 10 }}>
|
||||
<Typography
|
||||
style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}
|
||||
>
|
||||
@@ -2545,6 +2546,7 @@ const Billing = memo((props) => {
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
): null}
|
||||
</div>
|
||||
<div style={{ marginTop: 40, display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography
|
||||
|
||||
@@ -20,6 +20,10 @@ import {
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
Skeleton,
|
||||
Chip,
|
||||
Checkbox,
|
||||
MenuItem,
|
||||
DialogContent,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
@@ -46,6 +50,8 @@ import {
|
||||
Business as BusinessIcon,
|
||||
Visibility as VisibilityIcon,
|
||||
VisibilityOff as VisibilityOffIcon,
|
||||
CheckBox,
|
||||
Key,
|
||||
} from "@mui/icons-material";
|
||||
import { validateJson, } from "../views/Workflows.jsx";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
@@ -68,7 +74,7 @@ const scrollStyle2 = {
|
||||
|
||||
|
||||
const CacheView = memo((props) => {
|
||||
const { globalUrl, userdata, serverside, orgId, isSelectedDataStore } = props;
|
||||
const { globalUrl, userdata, serverside, orgId, isSelectedDataStore, selectedOrganization } = props;
|
||||
const [orgCache, setOrgCache] = React.useState("");
|
||||
const [listCache, setListCache] = React.useState([]);
|
||||
const [addCache, setAddCache] = React.useState("");
|
||||
@@ -82,12 +88,16 @@ const CacheView = memo((props) => {
|
||||
const [editCache, setEditCache] = React.useState(false);
|
||||
const [cachedLoaded, setCachedLoaded] = React.useState(false);
|
||||
const [show, setShow] = useState({});
|
||||
const [showDistributionPopup, setShowDistributionPopup] = useState(false);
|
||||
const [selectedSubOrg, setSelectedSubOrg] = useState([]);
|
||||
const [selectedCacheKey, setSelectedCacheKey] = useState("");
|
||||
useEffect(() => {
|
||||
if(orgId?.length >0){
|
||||
listOrgCache(orgId);
|
||||
}
|
||||
}, [orgId]);
|
||||
|
||||
|
||||
const listOrgCache = (orgId) => {
|
||||
fetch(globalUrl + `/api/v1/orgs/${orgId}/list_cache`, {
|
||||
method: "GET",
|
||||
@@ -214,7 +224,7 @@ const CacheView = memo((props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setAddCache(responseJson);
|
||||
toast("New Cache Added Successfully!");
|
||||
toast("New key Added Successfully!");
|
||||
listOrgCache(orgId);
|
||||
setModalOpen(false);
|
||||
})
|
||||
@@ -296,7 +306,7 @@ const CacheView = memo((props) => {
|
||||
>
|
||||
<DialogTitle>
|
||||
<span style={{ color: "white" }}>
|
||||
{ editCache ? "Edit Cache" : "Add Cache" }
|
||||
{ editCache ? "Edit Key" : "Add Key" }
|
||||
</span>
|
||||
</DialogTitle>
|
||||
<div style={{ paddingLeft: "30px", paddingRight: '30px', backgroundColor: "#212121", }}>
|
||||
@@ -368,6 +378,7 @@ const CacheView = memo((props) => {
|
||||
style={{ borderRadius: "2px", fontSize: 16, color: "#ff8544", textTransform:"none" }}
|
||||
onClick={() => {
|
||||
setModalOpen(false)
|
||||
setKey("")
|
||||
setValue("")
|
||||
setDataValue({})
|
||||
}}
|
||||
@@ -380,7 +391,7 @@ const CacheView = memo((props) => {
|
||||
style={{ borderRadius: "2px", backgroundColor: "#ff8544",color: "#1a1a1a", textTransform:"none" }}
|
||||
onClick={() => {
|
||||
{editCache ? editOrgCache(orgId) : addOrgCache(orgId)}
|
||||
|
||||
setKey("")
|
||||
setValue("")
|
||||
setDataValue({})
|
||||
}}
|
||||
@@ -392,9 +403,175 @@ const CacheView = memo((props) => {
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const handleSelectSubOrg = (id, action) => {
|
||||
if (action === "all") {
|
||||
const childOrgs = userdata.orgs.filter(
|
||||
(data) => data.creator_org === userdata.active_org.id
|
||||
);
|
||||
setSelectedSubOrg((prev) => {
|
||||
if (prev.length === childOrgs.length) {
|
||||
// If all child orgs are already selected, clear the selection
|
||||
return [];
|
||||
} else {
|
||||
// Otherwise, select all child org IDs
|
||||
return childOrgs.map((data) => data.id);
|
||||
}
|
||||
});
|
||||
} else if (action === "none") {
|
||||
setSelectedSubOrg([]);
|
||||
} else {
|
||||
setSelectedSubOrg((prev) => {
|
||||
if (prev.includes(id)) {
|
||||
return prev.filter((data) => data !== id);
|
||||
} else {
|
||||
return [...prev, id];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const changeDistribution = (id, selectedSubOrg) => {
|
||||
|
||||
editFileConfig(id, [...new Set(selectedSubOrg)])
|
||||
}
|
||||
|
||||
const editFileConfig = (id, selectedSubOrg, cacheKey) => {
|
||||
const data = {
|
||||
Key: id,
|
||||
action: "suborg_distribute",
|
||||
selected_suborgs: selectedSubOrg,
|
||||
}
|
||||
console.log("data: ", data);
|
||||
|
||||
const url = `${globalUrl}/api/v1/orgs/${orgId}/cache/config`;
|
||||
|
||||
fetch(url, {
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
toast("Failed overwriting datastore");
|
||||
} else {
|
||||
toast("Successfully updated datastore!");
|
||||
setTimeout(() => {
|
||||
listOrgCache(orgId);
|
||||
setShowDistributionPopup(false);
|
||||
}, 1000);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
toast("Err: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const cacheDistributionModal = showDistributionPopup ? (
|
||||
<Dialog
|
||||
open={showDistributionPopup}
|
||||
onClose={() => setShowDistributionPopup(false)}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
|
||||
border: theme?.palette?.DialogStyle?.border,
|
||||
fontFamily: theme?.typography?.fontFamily,
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
zIndex: 1000,
|
||||
minWidth: "600px",
|
||||
minHeight: "320px",
|
||||
overflow: "auto",
|
||||
'& .MuiDialogContent-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
'& .MuiDialogTitle-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
'& .MuiDialogActions-root': {
|
||||
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Select sub-org to distribute files
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
|
||||
<MenuItem value="all" onClick={()=> {handleSelectSubOrg(null, "all")}}>All</MenuItem>
|
||||
{userdata.orgs.map((data, index) => {
|
||||
if (data.creator_org !== userdata.active_org.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const imagesize = 22;
|
||||
const imageStyle = {
|
||||
width: imagesize,
|
||||
height: imagesize,
|
||||
pointerEvents: "none",
|
||||
marginRight: 10,
|
||||
marginLeft: data.id === userdata.active_org.id ? 0 : 20,
|
||||
};
|
||||
|
||||
const image = data.image === "" ? (
|
||||
<img alt={data.name} src={theme.palette.defaultImage} style={imageStyle} />
|
||||
) : (
|
||||
<img alt={data.name} src={data.image} style={imageStyle} />
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={index}
|
||||
value={data.id}
|
||||
onClick={() => handleSelectSubOrg(data.id)}
|
||||
style={{ display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<Checkbox
|
||||
checked={selectedSubOrg.includes(data.id)}
|
||||
/>
|
||||
{image}
|
||||
<span style={{ marginLeft: 8 }}>{data.name}</span>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
|
||||
<div style={{ display: "flex", marginTop: 20 }}>
|
||||
<Button
|
||||
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544" }}
|
||||
onClick={() => setShowDistributionPopup(false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#1a1a1a", backgroundColor: "#ff8544", marginLeft: 10 }}
|
||||
onClick={() => {
|
||||
changeDistribution(selectedCacheKey, selectedSubOrg);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div style={{paddingBottom: isSelectedDataStore?null:250, minHeight: 1000, boxSizing: "border-box", width: isSelectedDataStore? "100%" :null, transition: "width 0.3s ease", padding:isSelectedDataStore?"27px 10px 27px 27px":null, height: isSelectedDataStore?"100%":null, color: isSelectedDataStore?'#ffffff':null, backgroundColor: isSelectedDataStore?'#212121':null, borderTopRightRadius: isSelectedDataStore?'8px':null, borderBottomRightRadius: isSelectedDataStore?'8px':null, borderLeft: "1px solid #494949" }}>
|
||||
{modalView}
|
||||
{cacheDistributionModal}
|
||||
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
|
||||
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }}>
|
||||
<div style={{ marginTop: isSelectedDataStore?null:20, marginBottom: 20 }}>
|
||||
@@ -422,7 +599,7 @@ const CacheView = memo((props) => {
|
||||
setValue("")
|
||||
}}
|
||||
>
|
||||
Add Cache
|
||||
Add Key
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 16, marginRight: 15, backgroundColor: isSelectedDataStore?"#2F2F2F":null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null,borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
|
||||
@@ -459,7 +636,7 @@ const CacheView = memo((props) => {
|
||||
overflowX: "auto",
|
||||
}}>
|
||||
<ListItem style={{width: isSelectedDataStore?"100%":null, borderBottom:isSelectedDataStore?"1px solid #494949":null, display: "table-row"}}>
|
||||
{["Key", "Value", "Actions", "Updated"].map((header, index) => (
|
||||
{["Key", "Value", "Actions", "Updated", "Distribution"].map((header, index) => (
|
||||
<ListItemText
|
||||
key={index}
|
||||
primary={header}
|
||||
@@ -483,7 +660,7 @@ const CacheView = memo((props) => {
|
||||
backgroundColor: "#212121",
|
||||
}}
|
||||
>
|
||||
{Array(4)
|
||||
{Array(5)
|
||||
.fill()
|
||||
.map((_, colIndex) => (
|
||||
<ListItemText
|
||||
@@ -507,9 +684,21 @@ const CacheView = memo((props) => {
|
||||
</ListItem>
|
||||
))
|
||||
: listCache?.length === 0 ? (
|
||||
<Typography style={{ textAlign: "center", marginTop: 20, marginBottom: 20, minWidth: 1000, }}>
|
||||
No Keys Found
|
||||
</Typography>
|
||||
<ListItem style={{ display: "table-row" }}>
|
||||
{Array(5).fill().map((_, index) => (
|
||||
<ListItemText
|
||||
key={index}
|
||||
style={{
|
||||
display: "table-cell",
|
||||
padding: "8px",
|
||||
textAlign: index === 0 ? "center" : "left",
|
||||
}}
|
||||
primary={index === 2 ? "No key found." : null}
|
||||
colSpan={index === 0 ? 5 : undefined}
|
||||
/>
|
||||
))}
|
||||
</ListItem>
|
||||
|
||||
): listCache?.map((data, index) => {
|
||||
var bgColor = isSelectedDataStore? "#212121":"#27292d";
|
||||
if (index % 2 === 0) {
|
||||
@@ -517,6 +706,7 @@ const CacheView = memo((props) => {
|
||||
}
|
||||
|
||||
const validate = validateJson(data.value);
|
||||
const isDistributed = data?.suborg_distribution?.length > 0 ? true : false;
|
||||
return (
|
||||
<ListItem key={index} style={{display:'table-row', backgroundColor: bgColor, maxHeight: 300, overflow: "auto", borderBottomLeftRadius: listCache?.length - 1 === index ? 8 : 0, borderBottomRightRadius: listCache?.length - 1 === index ? 8 : 0,}}>
|
||||
<ListItemText
|
||||
@@ -576,13 +766,14 @@ const CacheView = memo((props) => {
|
||||
primary={(
|
||||
<span style={{ display: "inline" }}>
|
||||
<Tooltip
|
||||
title="Edit item"
|
||||
title={data?.org_id !== selectedOrganization.id ? "You can not edit this cache as it is controlled by parent organization." : "Edit this key" }
|
||||
style={{}}
|
||||
aria-label={"Edit"}
|
||||
>
|
||||
<span>
|
||||
<IconButton
|
||||
style={{ padding: "6px" }}
|
||||
disabled={data.org_id !== selectedOrganization.id ? true : false}
|
||||
onClick={() => {
|
||||
setEditCache(true)
|
||||
setDataValue({
|
||||
@@ -598,14 +789,14 @@ const CacheView = memo((props) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={"Public URL (types: text, raw, json)"}
|
||||
title={data?.org_id !== selectedOrganization.id ? "You can not access public URL for this key as it is controlled by parent organization." : "Public URL (types: text, raw, json)" }
|
||||
style={{ marginLeft: 0, }}
|
||||
aria-label={"Public URL"}
|
||||
>
|
||||
<span>
|
||||
<IconButton
|
||||
style={{ padding: "6px" }}
|
||||
disabled={data.public_authorization === undefined || data.public_authorization === null || data.public_authorization === "" ? true : false}
|
||||
disabled={data.public_authorization === undefined || data.public_authorization === null || data.public_authorization === "" || data.org_id !== selectedOrganization.id ? true : false}
|
||||
onClick={() => {
|
||||
window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank");
|
||||
}}
|
||||
@@ -616,18 +807,40 @@ const CacheView = memo((props) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={"Delete item"}
|
||||
title={data?.org_id !== selectedOrganization.id ? "You can not delete this key as it is controlled by parent organization." : "Delete this key" }
|
||||
aria-label={"Delete"}
|
||||
>
|
||||
<span>
|
||||
<IconButton
|
||||
style={{ padding: "6px" }}
|
||||
disabled={data.org_id !== selectedOrganization.id ? true : false}
|
||||
onClick={() => {
|
||||
deleteCache(orgId, data.key);
|
||||
//deleteFile(orgId);
|
||||
}}
|
||||
>
|
||||
<img src="/icons/deleteIcon.svg" alt="delete" />
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={{
|
||||
stroke: data.org_id === selectedOrganization.id ? "#fd4c62" : "#c8c8c8",
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M5 7.20001H6.6H19.4"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
<path
|
||||
d="M17.7996 7.2V18.4C17.7996 18.8243 17.631 19.2313 17.331 19.5314C17.0309 19.8314 16.624 20 16.1996 20H8.19961C7.77526 20 7.3683 19.8314 7.06824 19.5314C6.76818 19.2313 6.59961 18.8243 6.59961 18.4V7.2M8.99961 7.2V5.6C8.99961 5.17565 9.16818 4.76869 9.46824 4.46863C9.7683 4.16857 10.1753 4 10.5996 4H13.7996C14.224 4 14.6309 4.16857 14.931 4.46863C15.231 4.76869 15.3996 5.17565 15.3996 5.6V7.2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
</svg>
|
||||
</IconButton>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -642,6 +855,44 @@ const CacheView = memo((props) => {
|
||||
}}
|
||||
primary={new Date(data.edited * 1000).toISOString()}
|
||||
/>
|
||||
{selectedOrganization.id !== undefined && data?.org_id !== selectedOrganization.id ?
|
||||
<ListItemText
|
||||
primary={
|
||||
<Tooltip
|
||||
title="Parent organization controlled datastore. You can use, but not modify this key. Contact an admin of your parent organization if you need changes to this."
|
||||
placement="top"
|
||||
>
|
||||
<Chip
|
||||
label={"Parent"}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
style={{display: "table-cell", textAlign: 'center', verticalAlign: 'middle', }}
|
||||
/>
|
||||
:
|
||||
<Tooltip
|
||||
title="Distributed to sub-organizations. This means the sub organizations can use this datastore key, but can not modify it."
|
||||
placement="top"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={ userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" )? true : false}
|
||||
checked={isDistributed}
|
||||
style={{ }}
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setShowDistributionPopup(true)
|
||||
if(data?.suborg_distribution?.length > 0){
|
||||
setSelectedSubOrg(data.suborg_distribution)
|
||||
}else{
|
||||
setSelectedSubOrg([])
|
||||
}
|
||||
setSelectedCacheKey(data.key)
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
@@ -653,4 +904,4 @@ const CacheView = memo((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
export default memo(CacheView);
|
||||
export default memo(CacheView);
|
||||
|
||||
@@ -36,7 +36,8 @@ const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isD
|
||||
}
|
||||
|
||||
const action = folderDisabled ? "enable_folder" : "disable_folder";
|
||||
const url = `${globalUrl}/api/v1/detections/${action}`;
|
||||
//const url = `${globalUrl}/api/v1/detections/${detectionType}/selected_rules/${action}`;
|
||||
const url = `${globalUrl}/api/v1/detections/sigma/selected_rules/${action}`;
|
||||
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
@@ -258,7 +259,7 @@ const DetectionExplorer = (props) => {
|
||||
rule.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
|
||||
const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.data_lake?.enabled === true).length : 0
|
||||
const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.archived === false && env?.data_lake?.enabled === true).length : 0
|
||||
|
||||
return (
|
||||
<Container>
|
||||
@@ -284,7 +285,8 @@ const DetectionExplorer = (props) => {
|
||||
</Typography>
|
||||
|
||||
<div style={{display: "flex", }}>
|
||||
{workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
|
||||
|
||||
{/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
|
||||
<div style={{display: "flex", }}>
|
||||
<div style={{minWidth: 400, maxWidth: 400, }}>
|
||||
<WorkflowValidationTimeline
|
||||
@@ -313,7 +315,7 @@ const DetectionExplorer = (props) => {
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
:
|
||||
: */}
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
@@ -331,11 +333,11 @@ const DetectionExplorer = (props) => {
|
||||
detectionWorkflowId === "" ? `Connect to ${detectionInfo?.category}` :
|
||||
isDetectionValid ? `Connected to ${detectionInfo?.category}` : `Fix ${detectionInfo?.category} connection`}
|
||||
</Button>
|
||||
}
|
||||
{/**/}
|
||||
|
||||
{detectionInfo?.category === "SIGMA" || detectionInfo?.category === "SIEM" ?
|
||||
<Tooltip title={`You have ${lakeNodes} available Data Lake node(s)`}>
|
||||
<a href="/admin?tab=environments" style={{textDecoration: "none", color: "inherit", }} target="_blank" rel="noreferrer">
|
||||
<a href="/admin?tab=Locations" style={{textDecoration: "none", color: "inherit", }} target="_blank" rel="noreferrer">
|
||||
<FmdGoodIcon style={{marginLeft: 15, marginTop: 5, color: lakeNodes > 0 ? green : red}} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
@@ -137,6 +137,8 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
||||
|
||||
setResponseValue(e.target.value)
|
||||
|
||||
toast.error("The automatic response system is NOT available for you yet. Please contact support@shuffler.io if you want to try this feature.")
|
||||
|
||||
// FIXME: Handle:
|
||||
// 1. Get the current cache for the detection
|
||||
// 2. Create a new mapping for Detection -> Response
|
||||
@@ -187,7 +189,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
||||
<Switch
|
||||
checked={isEnabled && !folderDisabled}
|
||||
onChange={handleSwitchChange}
|
||||
disabled={false}
|
||||
disabled={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -647,7 +647,7 @@ const EditWorkflow = (props) => {
|
||||
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
||||
userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ?
|
||||
<Typography variant="body2" style={{ marginTop: 10, color: "rgba(255,255,255,0.7)" }}>
|
||||
Your organization does not have any suborgs yet OR your user may not have access to available suborgs. Please <a href="/admin?tab=suborgs" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">make one</a> or get access to suborgs by another admin, then try again.
|
||||
Your organization does not have any suborgs yet OR your user may not have access to available suborgs. Please <a href="/admin?tab=tenants" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">make one</a> or get access to suborgs by another admin, then try again.
|
||||
</Typography>
|
||||
:
|
||||
<Typography variant="body2" style={{ marginTop: 10, color: "rgba(255,255,255,0.7)" }}>
|
||||
@@ -738,7 +738,7 @@ const EditWorkflow = (props) => {
|
||||
})}
|
||||
</Select>
|
||||
:
|
||||
<Link to={"/admin?tab=suborgs"} style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">
|
||||
<Link to={"/admin?tab=tenants"} style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">
|
||||
<Typography variant="body2" style={{ marginTop: 10, }}>
|
||||
Create a sub-org to distribute workflows to suborgs.
|
||||
</Typography>
|
||||
@@ -753,7 +753,7 @@ const EditWorkflow = (props) => {
|
||||
Git Backup Repository
|
||||
</Typography>
|
||||
<Typography variant="body2" style={{ textAlign: "left", marginTop: 5, }} color="textSecondary">
|
||||
Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. <b>The repository and branch must already have been initialized</b>. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides your <a href="/admin?admin_tab=organization" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">default backup repository</a>. <a href="/docs/configuration#environment-variables" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">Credentials are encrypted.</a> Creates <a href="/admin?admin_tab=priorities" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">notifications</a> if it fails.
|
||||
Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. <b>The repository and branch must already have been initialized</b>. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides your <a href="/admin?admin_tab=org_config" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">default backup repository</a>. <a href="/docs/configuration#environment-variables" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">Credentials are encrypted.</a> Creates <a href="/admin?admin_tab=notifications" style={{ textDecoration: "none", color: "#f86a3e" }} target="_blank">notifications</a> if it fails.
|
||||
</Typography>
|
||||
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||
<Grid item xs={6} style={{}}>
|
||||
|
||||
@@ -1567,7 +1567,7 @@ const Files = memo((props) => {
|
||||
placement="top"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" ? true : false}
|
||||
disabled={userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" ) ? true : false}
|
||||
checked={isDistributed}
|
||||
style={{ }}
|
||||
color="secondary"
|
||||
|
||||
@@ -869,8 +869,8 @@ const LicencePopup = (props) => {
|
||||
:
|
||||
shuffleVariant === 0 ? "price_1PZPSSEJjT17t98NLJoTMYja" : "price_1PZPQuEJjT17t98N3yORUtd9"
|
||||
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success`
|
||||
const failUrl = `${window.location.origin}/pricing?admin_tab=billing&payment=failure`
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
|
||||
const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
|
||||
|
||||
console.log("Priceitem: ", priceItem, shuffleVariant)
|
||||
var checkoutObject = {
|
||||
@@ -888,7 +888,7 @@ const LicencePopup = (props) => {
|
||||
}
|
||||
|
||||
if (stripe === undefined || stripe === null || stripe.redirectToCheckout === undefined) {
|
||||
window.open("https://shuffler.io/admin?admin_tab=billing&payment=stripe_error", "_self")
|
||||
window.open("https://shuffler.io/admin?admin_tab=billingstats&payment=stripe_error", "_self")
|
||||
}
|
||||
|
||||
stripe.redirectToCheckout(checkoutObject)
|
||||
|
||||
@@ -465,7 +465,7 @@ const Header = (props) => {
|
||||
</Link>
|
||||
|
||||
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
|
||||
<Link to="/admin?admin_tab=priorities" style={hrefStyle}>
|
||||
<Link to="/admin?admin_tab=notifications" style={hrefStyle}>
|
||||
<MenuItem
|
||||
onClick={(event) => {
|
||||
handleClose();
|
||||
@@ -1107,7 +1107,7 @@ const Header = (props) => {
|
||||
);
|
||||
})}
|
||||
<Divider />
|
||||
<Link to="/admin?tab=suborgs" style={hrefStyle}>
|
||||
<Link to="/admin?tab=tenants" style={hrefStyle}>
|
||||
<MenuItem
|
||||
key={"add suborgs"}
|
||||
style={{
|
||||
|
||||
@@ -92,6 +92,7 @@ import {
|
||||
SquareFoot as SquareFootIcon,
|
||||
Storage as StorageIcon,
|
||||
Check as CheckIcon,
|
||||
PriorityHigh as PriorityHighIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
export const useStyles = makeStyles({
|
||||
@@ -165,6 +166,7 @@ const ParsedAction = (props) => {
|
||||
|
||||
expansionModalOpen,
|
||||
setExpansionModalOpen,
|
||||
fixExample,
|
||||
|
||||
listCache,
|
||||
setActiveDialog,
|
||||
@@ -258,7 +260,7 @@ const ParsedAction = (props) => {
|
||||
|
||||
if (param.required === false && param.name.startsWith("${") && param.name.endsWith("}")) {
|
||||
// Check if it's a required param
|
||||
param.autocompleted = false
|
||||
param.autocompleted = true
|
||||
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
||||
if (selectedAction.required_body_fields.includes(param.name)) {
|
||||
param.required = true
|
||||
@@ -281,6 +283,7 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
|
||||
if (param.field_active === true) {
|
||||
param.autocompleted = true
|
||||
generated_optional.push(param)
|
||||
continue
|
||||
}
|
||||
@@ -298,8 +301,8 @@ const ParsedAction = (props) => {
|
||||
const newparams = auth
|
||||
.concat(bodyfield)
|
||||
.concat(required)
|
||||
.concat(special_optional)
|
||||
.concat(generated_optional)
|
||||
.concat(special_optional)
|
||||
.concat(optional)
|
||||
|
||||
var newkeyorder = []
|
||||
@@ -1492,6 +1495,7 @@ const ParsedAction = (props) => {
|
||||
newAppname = newAppname.replaceAll("_", " ")
|
||||
}
|
||||
|
||||
var optionalFound = false
|
||||
return (
|
||||
<div style={appApiViewStyle} id="parsed_action_view">
|
||||
|
||||
@@ -3122,91 +3126,6 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
{/*
|
||||
<ButtonGroup fullWidth>
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title={
|
||||
"Show all body fields instead of the body itself"
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<Button
|
||||
variant={hideBody === true ? "outlined" : "contained"}
|
||||
color="primary"
|
||||
style={{textTransform: "none",}}
|
||||
onClick={() => {
|
||||
// Set localstorage
|
||||
localStorage.setItem("hideBody", "true")
|
||||
|
||||
setHideBody(false)
|
||||
const updatedParameters = selectedActionParameters.map((param) => {
|
||||
if (param.name === "body") {
|
||||
return {
|
||||
...param,
|
||||
id: "UNTOGGLED",
|
||||
}
|
||||
}
|
||||
|
||||
if (param.description === openApiFieldDesc) {
|
||||
// Check required fields here
|
||||
if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null && selectedAction.required_body_fields.length > 0) {
|
||||
// Look for the field name in the required_body_fields
|
||||
if (selectedAction.required_body_fields.includes(param.name)) {
|
||||
param.required = true
|
||||
} else {
|
||||
param.required = false
|
||||
}
|
||||
}
|
||||
|
||||
return { ...param, field_active: true }
|
||||
}
|
||||
|
||||
return param
|
||||
})
|
||||
|
||||
setSelectedActionParameters(updatedParameters)
|
||||
}}>
|
||||
Simplified
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
title={
|
||||
"Show the body as it is"
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<Button
|
||||
variant={hideBody === true ? "contained" : "outlined" }
|
||||
color="primary"
|
||||
style={{textTransform: "none",}}
|
||||
onClick={() => {
|
||||
localStorage.setItem("hideBody", "false")
|
||||
setHideBody(true)
|
||||
// Make sure the body field is shown
|
||||
const updatedParameters = selectedActionParameters.map((param) => {
|
||||
if (param.name === "body") {
|
||||
return {
|
||||
...param,
|
||||
id: "TOGGLED",
|
||||
}
|
||||
}
|
||||
|
||||
if (param.description === openApiFieldDesc) {
|
||||
return { ...param, field_active: false }
|
||||
}
|
||||
|
||||
return param
|
||||
})
|
||||
|
||||
setSelectedActionParameters(updatedParameters)
|
||||
}}>
|
||||
Advanced
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
*/}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -3551,7 +3470,7 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
)
|
||||
|
||||
// Finds headers from a string to be used for autocompletion
|
||||
const findHeaders = (inputdata) => {
|
||||
@@ -4087,9 +4006,24 @@ const ParsedAction = (props) => {
|
||||
};
|
||||
|
||||
var parsedPaths = [];
|
||||
if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
if (innerdata.type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof innerdata.value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
|
||||
const coverColor = "#82ccc3"
|
||||
//menuPosition.left -= 50
|
||||
@@ -4270,8 +4204,14 @@ const ParsedAction = (props) => {
|
||||
data.variant = "STATIC_VALUE"
|
||||
}
|
||||
|
||||
const isFirstOptional = optionalFound === false && data.configuration === false && data.required === false ? true : false
|
||||
if (optionalFound === false && data.configuration === false && data.required === false) {
|
||||
optionalFound = true
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={data.name}>
|
||||
<div key={data.name} style={{marginTop: isFirstOptional ? 55 : 5, }}>
|
||||
{isFirstOptional ? <Divider style={{backgroundColor: "rgba(255,255,255,0.1)", marginBottom: 20, }} /> : null}
|
||||
{showButtonField === true ? hideBodyButtonValue : null}
|
||||
<div
|
||||
style={{ marginTop: 20, marginBottom: 0, display: "flex" }}
|
||||
@@ -4297,6 +4237,20 @@ const ParsedAction = (props) => {
|
||||
) : null}
|
||||
|
||||
{hasAutocomplete === true ?
|
||||
data.field_active === true ?
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title={"This is an advanced field, simplified to make it easier to use. NOT required according to the API documentation."}
|
||||
placement="top"
|
||||
>
|
||||
<PriorityHighIcon
|
||||
style={{
|
||||
color: "rgba(255,255,255,0.5)" ,
|
||||
marginRight: 0,
|
||||
}}/>
|
||||
</Tooltip>
|
||||
|
||||
:
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title={"Field was autocompleted by Shuffle based on previous actions (same fields or parent nodes)"}
|
||||
@@ -4316,7 +4270,7 @@ const ParsedAction = (props) => {
|
||||
title={"Explore your keys in Datastore"}
|
||||
placement="top"
|
||||
>
|
||||
<a href="/admin?tab=cache" target="_blank" style={{textDecoration: "none"}}>
|
||||
<a href="/admin?tab=datastore" target="_blank" style={{textDecoration: "none"}}>
|
||||
<StorageIcon style={{
|
||||
color: "#FF8544",
|
||||
marginRight: 10,
|
||||
@@ -4351,14 +4305,16 @@ const ParsedAction = (props) => {
|
||||
parsedvalue = ""
|
||||
}
|
||||
|
||||
//console.log("Required fields: ", selectedActionParameters[count])
|
||||
|
||||
setEditorData({
|
||||
"name": data.name,
|
||||
"value": parsedvalue,
|
||||
"value": fixExample(parsedvalue),
|
||||
"field_number": count,
|
||||
"actionlist": actionlist,
|
||||
"field_id": clickedFieldId,
|
||||
|
||||
"example": selectedActionParameters[count].example,
|
||||
"example": fixExample(selectedActionParameters[count].example),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -387,7 +387,7 @@ const SearchData = props => {
|
||||
if (responseJson.success === false) {
|
||||
toast(`Failed to ${type} the app for your organization. Please try again or contact support@shuffler.io for more info`)
|
||||
} else {
|
||||
toast(`App successfully ${type}d. Please refresh the page to use it.`)
|
||||
toast(`App successfully ${type}d. It may now be used in your workflows.`)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
@@ -114,7 +114,9 @@ const CodeEditor = (props) => {
|
||||
editorData,
|
||||
|
||||
setAiQueryModalOpen,
|
||||
fullScreenMode
|
||||
fullScreenMode,
|
||||
environment,
|
||||
fixExample,
|
||||
} = props
|
||||
|
||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||
@@ -189,8 +191,27 @@ const CodeEditor = (props) => {
|
||||
tmpVariables.push('$' + actionlist[i].autocomplete.toLowerCase())
|
||||
|
||||
var parsedPaths = []
|
||||
if (typeof actionlist[i].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[i].example, "");
|
||||
if (actionlist[i].type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof actionlist[i].value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(actionlist[i].value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, skip parsing
|
||||
continue
|
||||
}
|
||||
} else if (typeof actionlist[i].value === "object") {
|
||||
// Direct object/array value
|
||||
parsedPaths = GetParsedPaths(actionlist[i].value, "");
|
||||
}
|
||||
} else {
|
||||
// Handle regular action results
|
||||
if (typeof actionlist[i].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[i].example, "");
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in parsedPaths) {
|
||||
@@ -596,7 +617,7 @@ const CodeEditor = (props) => {
|
||||
var code_lines = value.split('\n')
|
||||
for (var i = 0; i < code_lines.length; i++) {
|
||||
var current_code_line = code_lines[i]
|
||||
var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g);
|
||||
var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g)
|
||||
|
||||
if (!variable_occurence) {
|
||||
continue;
|
||||
@@ -664,6 +685,75 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
var code_lines = value.split('\n')
|
||||
for (var i = 0; i < code_lines.length; i++) {
|
||||
var current_code_line = code_lines[i]
|
||||
|
||||
// Look for REPLACE_ME
|
||||
var variable_occurence = current_code_line.match(/REPLACE_ME/g)
|
||||
|
||||
if (!variable_occurence) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var new_occurences = variable_occurence.filter((occurrence) => occurrence[0])
|
||||
variable_occurence = new_occurences
|
||||
|
||||
// Find the start position of REPLACE_ME and highlight it
|
||||
var dollar_occurence = []
|
||||
for (let ch = 0; ch < current_code_line.length; ch++) {
|
||||
// Not allowing it then lol
|
||||
if (ch + 9 >= current_code_line.length) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Rofl - at least it is specific
|
||||
if (current_code_line[ch] === 'R' && current_code_line[ch + 1] === 'E' && current_code_line[ch + 2] === 'P' && current_code_line[ch + 3] === 'L' && current_code_line[ch + 4] === 'A' && current_code_line[ch + 5] === 'C' && current_code_line[ch + 6] === 'E' && current_code_line[ch + 7] === '_' && current_code_line[ch + 8] === 'M' && current_code_line[ch + 9] === 'E') {
|
||||
dollar_occurence.push(ch)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (variable_occurence.length === 0) {
|
||||
//value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #282828; border-radius: 0px; color: #b8bb26"})
|
||||
//value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #; border-radius: 0px; color: inherit"})
|
||||
}
|
||||
|
||||
for (let occ = 0; occ < variable_occurence.length; occ++) {
|
||||
const fixedVariable = variable_occurence[occ]
|
||||
|
||||
var startCh = dollar_occurence[occ]
|
||||
var endCh = dollar_occurence[occ] + 10
|
||||
try {
|
||||
newMarkers.push({
|
||||
startRow: i,
|
||||
startCol: startCh,
|
||||
endRow: i,
|
||||
endCol: endCh,
|
||||
className: "bad-marker",
|
||||
type: "text",
|
||||
})
|
||||
} catch (e) {
|
||||
console.log("Error in color highlighting: ", e);
|
||||
newMarkers.push({
|
||||
startRow: i,
|
||||
startCol: startCh,
|
||||
endRow: i,
|
||||
endCol: endCh,
|
||||
className: "bad-marker",
|
||||
type: "text",
|
||||
})
|
||||
}
|
||||
|
||||
setMarkers(newMarkers)
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
console.log("Error in color highlighting: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
setMarkers(newMarkers)
|
||||
}
|
||||
|
||||
@@ -720,6 +810,34 @@ const CodeEditor = (props) => {
|
||||
const fixedVariable = fixVariable(found[i])
|
||||
|
||||
var valuefound = false
|
||||
|
||||
// First check if it's a workflow variable
|
||||
if (actionlist !== undefined && actionlist.length > 0) {
|
||||
const workflowVar = actionlist?.find(item =>
|
||||
item.type === "workflow_variable" &&
|
||||
`$${item.autocomplete.toLowerCase()}` === fixedVariable.toLowerCase()
|
||||
)
|
||||
|
||||
if (workflowVar && workflowVar.example) {
|
||||
valuefound = true
|
||||
try {
|
||||
// Try to parse the example value if it's stored as a JSON string
|
||||
if (typeof workflowVar.example === "string" &&
|
||||
(workflowVar.example.startsWith("[") || workflowVar.example.startsWith("{"))) {
|
||||
const parsedExample = JSON.parse(workflowVar.example)
|
||||
input = input.replace(found[i], JSON.stringify(parsedExample), -1)
|
||||
} else {
|
||||
input = input.replace(found[i], workflowVar.example, -1)
|
||||
}
|
||||
continue
|
||||
} catch (e) {
|
||||
console.log("Error parsing workflow variable:", e)
|
||||
input = input.replace(found[i], workflowVar.example, -1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < actionlist.length; j++) {
|
||||
if (fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()) {
|
||||
continue
|
||||
@@ -750,9 +868,26 @@ const CodeEditor = (props) => {
|
||||
var shouldbreak = false
|
||||
for (var k = 0; k < actionlist.length; k++) {
|
||||
var parsedPaths = []
|
||||
if (typeof actionlist[k].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].example, "");
|
||||
}
|
||||
|
||||
// Handle both workflow variables and regular actions
|
||||
if (actionlist[k].type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(actionlist[k].value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(actionlist[k].value, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].value === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].value, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].example, "");
|
||||
}
|
||||
|
||||
for (var key in parsedPaths) {
|
||||
const fullpath = "$" + actionlist[k].autocomplete.toLowerCase() + parsedPaths[key].autocomplete.toLowerCase()
|
||||
@@ -766,7 +901,22 @@ const CodeEditor = (props) => {
|
||||
|
||||
var new_input = ""
|
||||
try {
|
||||
new_input = FindJsonPath(fullpath, actionlist[k].example)
|
||||
const sourceData = actionlist[k].type === "workflow_variable" ?
|
||||
(() => {
|
||||
// Try to parse the value if it's a JSON string
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
return JSON.parse(actionlist[k].value);
|
||||
} catch (e) {
|
||||
// If parsing fails, return the original string value
|
||||
return actionlist[k].value;
|
||||
}
|
||||
}
|
||||
return actionlist[k].value;
|
||||
})() :
|
||||
actionlist[k].example;
|
||||
|
||||
new_input = FindJsonPath(fullpath, sourceData)
|
||||
} catch (e) {
|
||||
console.log("ERR IN INPUT: ", e)
|
||||
}
|
||||
@@ -789,8 +939,9 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
input = input.replace(fixedVariable, new_input, -1)
|
||||
input = input.replace(found[i], new_input, -1)
|
||||
// Replace both the fixed and original variable to handle both #0 and # cases
|
||||
input = input.replace(found[i], new_input)
|
||||
input = input.replace(fixedVariable, new_input)
|
||||
|
||||
shouldbreak = true
|
||||
break
|
||||
@@ -876,8 +1027,12 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
if (edited === false) {
|
||||
if (!item.value.includes("{%") && !item.value.includes("{{")) {
|
||||
setlocalcodedata(localcodedata + " | " + item.value + " }}")
|
||||
if (item.value.includes("{%") || item.value.includes("{{")) {
|
||||
if (!item.value.includes("}}") && !item.value.includes("%}")) {
|
||||
setlocalcodedata(localcodedata + " | " + item.value + " }}")
|
||||
} else {
|
||||
setlocalcodedata(localcodedata + item.value)
|
||||
}
|
||||
} else {
|
||||
setlocalcodedata(localcodedata + item.value)
|
||||
}
|
||||
@@ -899,7 +1054,7 @@ const CodeEditor = (props) => {
|
||||
const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : selectedAction.name === "execute_bash" ? "execute_bash" : "repeat_back_to_me"
|
||||
const params = actionname === "execute_python" ? [{ "name": "code", "value": inputdata }] : actionname === "execute_bash" ? [{ "name": "code", "value": inputdata }, { "name": "shuffle_input", "value": "", }] : [{ "name": "call", "value": inputdata }]
|
||||
|
||||
const actiondata = { "description": "Repeats the call parameter", "id": "", "name": actionname, "label": "", "node_type": "", "environment": "", "sharing": false, "private_id": "", "public_id": "", "app_id": appid, "tags": null, "authentication": [], "tested": false, "parameters": params, "execution_variable": { "description": "", "id": "", "name": "", "value": "" }, "returns": { "description": "", "example": "", "id": "", "schema": { "type": "string" } }, "authentication_id": "", "example": "", "auth_not_required": false, "source_workflow": "", "run_magic_output": false, "run_magic_input": false, "execution_delay": 0, "app_name": "Shuffle Tools", "app_version": "1.2.0", "selectedAuthentication": {} }
|
||||
const actiondata = { "description": "Repeats the call parameter", "id": "", "name": actionname, "label": "", "node_type": "", "environment": environment?.Name, "sharing": false, "private_id": "", "public_id": "", "app_id": appid, "tags": null, "authentication": [], "tested": false, "parameters": params, "execution_variable": { "description": "", "id": "", "name": "", "value": "" }, "returns": { "description": "", "example": "", "id": "", "schema": { "type": "string" } }, "authentication_id": "", "example": "", "auth_not_required": false, "source_workflow": "", "run_magic_output": false, "run_magic_input": false, "execution_delay": 0, "app_name": "Shuffle Tools", "app_version": "1.2.0", "selectedAuthentication": {} }
|
||||
|
||||
setExecutionResult({
|
||||
"valid": false,
|
||||
@@ -1396,7 +1551,22 @@ const CodeEditor = (props) => {
|
||||
};
|
||||
|
||||
var parsedPaths = [];
|
||||
if (typeof innerdata.example === "object") {
|
||||
if (innerdata.type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof innerdata.value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
|
||||
@@ -1585,7 +1755,13 @@ const CodeEditor = (props) => {
|
||||
}}
|
||||
disabled={editorData === undefined || editorData.example === undefined || editorData.example === null || editorData.example.length === 0}
|
||||
onClick={() => {
|
||||
setlocalcodedata(editorData.example)
|
||||
if (fixExample !== undefined) {
|
||||
const newExample = fixExample(editorData.example)
|
||||
setlocalcodedata(newExample)
|
||||
} else {
|
||||
console.log("No fix example available!")
|
||||
setlocalcodedata(editorData.example)
|
||||
}
|
||||
}}
|
||||
color="secondary"
|
||||
>
|
||||
|
||||
@@ -11,29 +11,8 @@ const Admin2 = (props) => {
|
||||
const [organizationFeatures, setOrganizationFeatures] = useState({});
|
||||
const [orgRequest, setOrgRequest] = React.useState(true);
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
const handleGetOrg = (orgId) => {
|
||||
// if (
|
||||
// serverside !== true &&
|
||||
// window.location.search !== undefined &&
|
||||
// window.location.search !== null
|
||||
// ) {
|
||||
// const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
// const params = Object.fromEntries(urlSearchParams.entries());
|
||||
// const foundorgid = params["org_id"];
|
||||
// if (foundorgid !== undefined && foundorgid !== null) {
|
||||
// orgId = foundorgid;
|
||||
// }
|
||||
// }
|
||||
console.log("getting organization details for: ", orgId);
|
||||
|
||||
// if (orgId === undefined) {
|
||||
// toast(
|
||||
// "Organization ID not defined. Please contact us on https://shuffler.io if this persists logout.",
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Just use this one?
|
||||
|
||||
fetch(`${globalUrl}/api/v1/orgs/${orgId}`, {
|
||||
method: "GET",
|
||||
|
||||
@@ -568,6 +568,9 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
}, [editWorkflowModalOpen])
|
||||
|
||||
const dragRef = React.useRef(false);
|
||||
|
||||
|
||||
// New for generated stuff
|
||||
const releaseToConnectLabel = "Release to Connect"
|
||||
const integrationApps = [{
|
||||
@@ -696,11 +699,32 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}
|
||||
|
||||
if (loadedApps.includes(appId)) {
|
||||
return
|
||||
console.log("App already loaded: ", appId)
|
||||
|
||||
// 1. Find the app and check if it has actions
|
||||
// 2. If it doesn't have actions, reload once again
|
||||
var should_reload = false
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
const curapp = apps[i]
|
||||
if (curapp.id !== appId) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (curapp.actions === undefined || curapp.actions === null || curapp.actions.length === 0 || curapp.actions.length === 1) {
|
||||
should_reload = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_reload) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
loadedApps.push(appId)
|
||||
setLoadedApps(loadedApps)
|
||||
if (!loadedApps.includes(appId)) {
|
||||
loadedApps.push(appId)
|
||||
setLoadedApps(loadedApps)
|
||||
}
|
||||
|
||||
const appUrl = `${globalUrl}/api/v1/apps/${appId}/config?openapi=false`
|
||||
fetch(appUrl, {
|
||||
@@ -2331,6 +2355,35 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
return success
|
||||
};
|
||||
|
||||
const fixExample = (input, required) => {
|
||||
if (input === undefined || input === null || input.length === 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
// 1. Find anything matching ${variable}
|
||||
// 2. If it is required, replace it with REQUIRED
|
||||
// 3. If it is not required, replace it with empty
|
||||
|
||||
// Check if it is a string or not
|
||||
var newExample = input
|
||||
if (typeof newExample !== "string") {
|
||||
return newExample
|
||||
}
|
||||
|
||||
const found = newExample.match(/\${(.*?)}/g)
|
||||
if (found === null || found === undefined || found.length === 0) {
|
||||
return newExample
|
||||
}
|
||||
|
||||
for (var i = 0; i < found.length; i++) {
|
||||
//newExample = newExample.replace(found[i], "REQUIRED")
|
||||
newExample = newExample.replace(found[i], "REPLACE_ME")
|
||||
}
|
||||
|
||||
return newExample
|
||||
}
|
||||
|
||||
const monitorUpdates = () => {
|
||||
var firstnode = cy.getElementById(workflow.start);
|
||||
if (firstnode.length === 0) {
|
||||
@@ -3656,7 +3709,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
setWorkflows([responseJson])
|
||||
} else {
|
||||
getAppAuthentication();
|
||||
getEnvironments();
|
||||
getEnvironments(responseJson.org_id)
|
||||
|
||||
getSettings();
|
||||
getFiles()
|
||||
@@ -6864,7 +6917,9 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}
|
||||
}
|
||||
|
||||
if (showEnvCnt > 1) {
|
||||
// Always showing for now
|
||||
//if (showEnvCnt > 1) {
|
||||
if (showEnvCnt > 0) {
|
||||
setShowEnvironment(true)
|
||||
}
|
||||
|
||||
@@ -10018,15 +10073,37 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
|
||||
const ParsedAppPaper = (props) => {
|
||||
const app = props.app
|
||||
const small = props.small
|
||||
const actionString = props.action
|
||||
const small = props.small
|
||||
const actionString = props.action
|
||||
|
||||
const [hover, setHover] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
if(app.name === "Shuffle Tools"){
|
||||
if (app.actions !== undefined && (app.actions === null || app.actions.length === 1)) {
|
||||
loadAppConfig(app.id, false)
|
||||
|
||||
// Prevent hover effects during drag
|
||||
const handleMouseMove = React.useCallback((e) => {
|
||||
if (dragRef.current) {
|
||||
setHover(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Add mousemove listener to track dragging
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
};
|
||||
}, [handleMouseMove]);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.skip_load === true) {
|
||||
return
|
||||
}
|
||||
|
||||
if (app.name === "Shuffle Tools"){
|
||||
if (app.actions !== undefined && (app.actions === null || app.actions.length === 1)) {
|
||||
loadAppConfig(app.id, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (app === undefined || app === null) {
|
||||
@@ -10115,13 +10192,20 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}
|
||||
|
||||
newAppStyle.backgroundColor = theme.palette.backgroundColor
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
onStart={() => {
|
||||
dragRef.current = true;
|
||||
newAppStyle.zIndex = 9999
|
||||
|
||||
}}
|
||||
onDrag={(e) => {
|
||||
newAppStyle.zIndex = 9999
|
||||
handleAppDrag(e, app)
|
||||
}}
|
||||
onStop={(e) => {
|
||||
dragRef.current = false;
|
||||
newAppStyle.zIndex = "none"
|
||||
handleDragStop(e, app)
|
||||
}}
|
||||
key={app.id}
|
||||
@@ -10132,18 +10216,18 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
<Paper
|
||||
square
|
||||
style={newAppStyle}
|
||||
onMouseOver={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
setHover(true)
|
||||
|
||||
if (app.actions !== undefined && (app.actions === null || app.actions.length === 1)) {
|
||||
loadAppConfig(app.id, false)
|
||||
}
|
||||
|
||||
onMouseEnter={(e) => {
|
||||
if (!dragRef.current) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setHover(true);
|
||||
|
||||
if (app.actions !== undefined && (app.actions === null || app.actions.length === 1)) {
|
||||
loadAppConfig(app.id, false);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onMouseOut={() => {
|
||||
onMouseLeave={() => {
|
||||
setHover(false);
|
||||
}}
|
||||
onClick={() => {
|
||||
@@ -10586,6 +10670,27 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}
|
||||
|
||||
var viewedApps = []
|
||||
|
||||
const QuickAccessSection = ({title, items, renderItem}) => (
|
||||
<div style={{marginBottom: 25}}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
color="textSecondary"
|
||||
style={{marginTop: 20, marginLeft: 5}}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<div style={{display: "flex", flexWrap: "wrap", gap: 5}}>
|
||||
{items.map((item, index) => renderItem(item, index))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Popular Shuffle Tools actions
|
||||
const popularActions = [
|
||||
["repeat_back_to_me", "filter_list", "execute_python", "parse_ioc"],
|
||||
["set_cache_value", "get_file_meta", "merge_lists", "send_sms_shuffle"]
|
||||
];
|
||||
return (
|
||||
<div style={appViewStyle}>
|
||||
<div style={{ flex: "1", zoom: 0.9, }}>
|
||||
@@ -10628,59 +10733,37 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}}
|
||||
/>
|
||||
|
||||
{shuffleToolsApp !== undefined && shuffleToolsApp !== null && document?.getElementById("appsearch")?.value?.length === 0 ?
|
||||
<div style={{marginBottom: 25, }}>
|
||||
<Typography variant="body1" color="textSecondary" style={{marginTop: 20, marginLeft: 5, }}>
|
||||
Popular Actions
|
||||
</Typography>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
}}>
|
||||
<ParsedAppPaper small={true} action={"repeat_back_to_me"} app={shuffleToolsApp} />
|
||||
<div style={{marginLeft: 5, }}/>
|
||||
<ParsedAppPaper small={true} action={"filter_list"} app={shuffleToolsApp} />
|
||||
<div style={{marginLeft: 5, }}/>
|
||||
<ParsedAppPaper small={true} action={"execute_python"} app={shuffleToolsApp} />
|
||||
<div style={{marginLeft: 5, }}/>
|
||||
<ParsedAppPaper small={true} action={"parse_ioc"} app={shuffleToolsApp} />
|
||||
</div>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
}}>
|
||||
<ParsedAppPaper small={true} action={"set_cache_value"} app={shuffleToolsApp} />
|
||||
<div style={{marginLeft: 5, }}/>
|
||||
<ParsedAppPaper small={true} action={"get_file_meta"} app={shuffleToolsApp} />
|
||||
<div style={{marginLeft: 5, }}/>
|
||||
<ParsedAppPaper small={true} action={"merge_lists"} app={shuffleToolsApp} />
|
||||
<div style={{marginLeft: 5, }}/>
|
||||
<ParsedAppPaper small={true} action={"send_sms_shuffle"} app={shuffleToolsApp} />
|
||||
</div>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
{document?.getElementById("appsearch")?.value?.length === 0 ?
|
||||
<div style={{marginBottom: 25, }}>
|
||||
<Typography variant="body1" color="textSecondary" style={{marginTop: 20, marginLeft: 5, }}>
|
||||
Triggers
|
||||
</Typography>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
}}>
|
||||
{triggers.map((trigger, index) => {
|
||||
if (trigger.trigger_type === "PIPELINE") {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
return(
|
||||
<div key={index} style={{marginLeft: index !== 0 ? 5 : 0, }}>
|
||||
<ParsedAppPaper small={true} action={trigger.name} app={JSON.parse(JSON.stringify(trigger))} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
: null}
|
||||
{shuffleToolsApp && !document?.getElementById("appsearch")?.value?.length && (
|
||||
<QuickAccessSection
|
||||
title="Popular Actions"
|
||||
items={popularActions.flat()}
|
||||
renderItem={(action) => (
|
||||
<ParsedAppPaper
|
||||
key={action}
|
||||
small={true}
|
||||
action={action}
|
||||
app={shuffleToolsApp}
|
||||
skip_load={true}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!document?.getElementById("appsearch")?.value?.length && (
|
||||
<QuickAccessSection
|
||||
title="Triggers"
|
||||
items={triggers.filter(t => t.trigger_type !== "PIPELINE")}
|
||||
renderItem={(trigger, index) => (
|
||||
<ParsedAppPaper
|
||||
key={index}
|
||||
small={true}
|
||||
action={trigger.name}
|
||||
app={JSON.parse(JSON.stringify(trigger))}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Typography variant="body1" color="textSecondary" style={{marginTop: 20, marginLeft: 5, }}>
|
||||
Your Apps
|
||||
@@ -13942,8 +14025,23 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
};
|
||||
|
||||
var parsedPaths = [];
|
||||
console.log("Found example data: ", innerdata.example)
|
||||
if (typeof innerdata.example === "object") {
|
||||
|
||||
if (innerdata.type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof innerdata.value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
|
||||
@@ -16531,7 +16629,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}
|
||||
</div>
|
||||
|
||||
{showEnvironment === true && environments.length > 1 && selectedActionEnvironment !== undefined && selectedActionEnvironment !== null && selectedActionEnvironment.Name !== undefined && selectedActionEnvironment.Name !== null ?
|
||||
{showEnvironment === true && environments.length > 0 && selectedActionEnvironment !== undefined && selectedActionEnvironment !== null && selectedActionEnvironment.Name !== undefined && selectedActionEnvironment.Name !== null ?
|
||||
<FormControl fullWidth style={{marginTop: 15, pointerEvents: "auto", maxWidth: 250, }}>
|
||||
|
||||
<InputLabel
|
||||
@@ -16556,6 +16654,10 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
height: 40,
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
console.log("CLICK!")
|
||||
getEnvironments(workflow.org_id)
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setLastSaved(false)
|
||||
const env = environments.find((a) => a.Name === e.target.value);
|
||||
@@ -16600,8 +16702,9 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
>
|
||||
|
||||
{data.Name === "cloud" || data.Name === "Cloud" ? null : !isRunning ?
|
||||
|
||||
<a href={`/admin?tab=locations&env=${data.Name}`} target="_blank" style={{textDecoration: "none",}}>
|
||||
<Tooltip title={"Click to configure this location"} placement="top">
|
||||
<Tooltip title={"Click to configure this runtime location"} placement="top">
|
||||
<Chip
|
||||
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer", backgroundColor: red, }}
|
||||
label={"Stopped"}
|
||||
@@ -16610,6 +16713,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
window.open(`/admin?tab=locations&env=${data.Name}`, "_blank", "noopener,noreferrer")
|
||||
}}
|
||||
|
||||
@@ -16617,7 +16721,21 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
/>
|
||||
</Tooltip>
|
||||
</a>
|
||||
: null}
|
||||
:
|
||||
<Chip
|
||||
key={index}
|
||||
style={{
|
||||
color: green,
|
||||
borderColor: green,
|
||||
}}
|
||||
label={"Running"}
|
||||
onClick={() => {
|
||||
//handleChipClick
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
|
||||
{data.default === true ?
|
||||
<Chip
|
||||
@@ -18000,7 +18118,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
setWorkflow(workflow)
|
||||
|
||||
getAppAuthentication();
|
||||
getEnvironments();
|
||||
getEnvironments(workflow.org_id)
|
||||
getWorkflowExecution(props.match.params.key, "");
|
||||
getAvailableWorkflows(-1);
|
||||
getSettings();
|
||||
@@ -19020,7 +19138,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
window.open(`/admin?admin_tab=priorities&workflow=${data.workflow.id}&execution_id=${data.execution_id}`, "_blank")
|
||||
window.open(`/admin?admin_tab=notifications&workflow=${data.workflow.id}&execution_id=${data.execution_id}`, "_blank")
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -19075,7 +19193,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ padding: isMobile ? "0px 10px 25px 10px" : "25px 15px 25px 15px", maxWidth: isMobile ? "100%" : "100%", overflowX: "hidden" }}>
|
||||
<div style={{ padding: isMobile ? "0px 10px 50px 10px" : "25px 15px 150px 15px", maxWidth: isMobile ? "100%" : "100%", overflowX: "hidden", }}>
|
||||
|
||||
|
||||
<Breadcrumbs
|
||||
@@ -19129,7 +19247,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
marginBottom: 10,
|
||||
}}
|
||||
/>
|
||||
<div style={{ display: "flex", marginLeft: 10, }}>
|
||||
<div style={{ display: "flex", paddingLeft: 10, paddingRight: 10, position: "sticky", top: 0, zIndex: 12500, backgroundColor: theme.palette.platformColor, borderRadius: theme.palette.borderRadius, border: "2px solid rgba(255,255,255,0.3)", marginBottom: 10, }}>
|
||||
<h2>Details</h2>
|
||||
<Tooltip
|
||||
color="primary"
|
||||
@@ -19269,7 +19387,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
window.open(`/admin?admin_tab=priorities&workflow=${executionData.workflow.id}&execution_id=${executionData.execution_id}`, "_blank")
|
||||
window.open(`/admin?admin_tab=notifications&workflow=${executionData.workflow.id}&execution_id=${executionData.execution_id}`, "_blank")
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
@@ -20190,7 +20308,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}
|
||||
|
||||
if (stringjson.includes("kms/")) {
|
||||
return "KMS authentication most likely failed. Check your notifications for more details on this page: /admin?admin_tab=priorities. If you need help with KMS, please contact support@shuffler.io"
|
||||
return "KMS authentication most likely failed. Check your notifications for more details on this page: /admin?admin_tab=notifications. If you need help with KMS, please contact support@shuffler.io"
|
||||
}
|
||||
|
||||
if (stringjson.includes("invalidurl")) {
|
||||
@@ -20208,7 +20326,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
}
|
||||
|
||||
if (isCloud && stringjson.toLowerCase().includes("timeout error")) {
|
||||
return "Run this workflow in a local environment to increase the timeout. Go to https://shuffler.io/admin?tab=locationsto create an environment to connect to"
|
||||
return "Run this workflow in a local environment to increase the timeout. Go to https://shuffler.io/admin?tab=locations to create an environment to connect to"
|
||||
}
|
||||
|
||||
if (stringjson.toLowerCase().includes("invalid header")) {
|
||||
@@ -20218,7 +20336,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
|
||||
if (stringjson.includes("connectionerror")) {
|
||||
if (stringjson.includes("kms")) {
|
||||
return "KMS authentication most likely failed (2). Check your notifications for more details on this page: /admin?admin_tab=priorities&kms=true. If you need help with KMS, please contact support@shuffler.io"
|
||||
return "KMS authentication most likely failed (2). Check your notifications for more details on this page: /admin?admin_tab=notifications&kms=true. If you need help with KMS, please contact support@shuffler.io"
|
||||
}
|
||||
|
||||
return "The URL is incorrect, or Shuffle can't reach it. Set up a Shuffle Environment in the same VLAN, or whitelist Shuffle's IPs."
|
||||
@@ -20696,6 +20814,7 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
setExpansionModalOpen={setCodeEditorModalOpen}
|
||||
setEditorData={setEditorData}
|
||||
setAiQueryModalOpen={setAiQueryModalOpen}
|
||||
fixExample={fixExample}
|
||||
/>
|
||||
</div>
|
||||
</Fade>
|
||||
@@ -22583,6 +22702,8 @@ const releaseToConnectLabel = "Release to Connect"
|
||||
changeActionParameterCodeMirror={changeActionParameterCodeMirror}
|
||||
activeDialog={activeDialog}
|
||||
setActiveDialog={setActiveDialog}
|
||||
environment={selectedActionEnvironment}
|
||||
|
||||
|
||||
setAiQueryModalOpen={setAiQueryModalOpen}
|
||||
/>
|
||||
|
||||
@@ -101,8 +101,8 @@ const ApiExplorerWrapper = (props) => {
|
||||
}, [selectedAppData, openapi])
|
||||
|
||||
useEffect(() => {
|
||||
getAppData(appid)
|
||||
if (appid !== undefined && appid !== null && appid.length !== 0) {
|
||||
getAppData(appid)
|
||||
HandleGetLocations()
|
||||
}
|
||||
|
||||
@@ -157,13 +157,17 @@ const ApiExplorerWrapper = (props) => {
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
toast.error("Failed to get app data or App doesn't exist (1). Redirecting..");
|
||||
toast.error(`Failed to get API data for '${appname}' (1). Contact support@shuffler.io if this persists.`, {
|
||||
"autoClose": 10000,
|
||||
})
|
||||
setTimeout(()=>{
|
||||
navigate("/search?tab=apps");
|
||||
},3000)
|
||||
}
|
||||
} else {
|
||||
toast.error("Failed to get app data or App doesn't exist (2). Redirecting..");
|
||||
toast.error(`Failed to get API data for '${appname}' (2). Contact support@shuffler.io if this persists.`, {
|
||||
"autoClose": 10000,
|
||||
})
|
||||
setTimeout(()=>{
|
||||
navigate("/search?tab=apps");
|
||||
},3000)
|
||||
@@ -177,6 +181,11 @@ const ApiExplorerWrapper = (props) => {
|
||||
// Fetch data when appid is available
|
||||
const getAppData = useCallback((appid) => {
|
||||
if (appid === undefined || appid === null || appid.length === 0) {
|
||||
toast.error("No API data to load (4). Please contact support@shuffler.io if this persists.")
|
||||
|
||||
setTimeout(() => {
|
||||
navigate("/search?tab=apps")
|
||||
}, 3000)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -197,9 +206,9 @@ const ApiExplorerWrapper = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
toast.error("Failed to get app data or App doesn't exist (3). Redirecting..");
|
||||
setTimeout(()=>{
|
||||
navigate("/search?tab=apps");
|
||||
},3000)
|
||||
setTimeout(() => {
|
||||
navigate("/search?tab=apps")
|
||||
}, 3000)
|
||||
return;
|
||||
}
|
||||
return response.json();
|
||||
@@ -1790,7 +1799,7 @@ const Wrapper = ({children, isLoaded,isLoggedIn})=>{
|
||||
|
||||
return(
|
||||
|
||||
<div className="api-explorer-wrapper" style={{ paddingLeft: (isLoggedIn && isLoaded) ? leftSideBarOpenByClick ? 280 : 100 : 0, transition: 'padding-left 0.3s ease' }}>
|
||||
<div className="api-explorer-wrapper" style={{ zoom: 0.8, paddingLeft: (isLoggedIn && isLoaded) ? leftSideBarOpenByClick ? 280 : 100 : 0, transition: 'padding-left 0.3s ease' }}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
+13
-94
@@ -383,6 +383,7 @@ const Docs = (defaultprops) => {
|
||||
if (hash.includes('?')) {
|
||||
hash = hash.split('?')[0]
|
||||
}
|
||||
|
||||
if (hash) {
|
||||
const element = document.getElementById(hash.toLowerCase())
|
||||
if (element) {
|
||||
@@ -526,7 +527,7 @@ const Docs = (defaultprops) => {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
padding: 15,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginBottom: 30,
|
||||
marginBottom: 25,
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
@@ -618,7 +619,8 @@ const Docs = (defaultprops) => {
|
||||
<Divider
|
||||
style={{
|
||||
width: "90%",
|
||||
marginTop: 40,
|
||||
marginTop: 60,
|
||||
marginBottom: 20,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
/>
|
||||
@@ -664,6 +666,8 @@ const Docs = (defaultprops) => {
|
||||
minHeight: "93vh",
|
||||
maxHeight: "93vh",
|
||||
marginTop: 70,
|
||||
maxWidth: 250,
|
||||
overflow: "hidden",
|
||||
}
|
||||
|
||||
const fetchDocList = () => {
|
||||
@@ -705,8 +709,7 @@ const Docs = (defaultprops) => {
|
||||
// Find <img> tags and translate them into ![]() format
|
||||
const imgRegex = /<img.*?src="(.*?)"/g;
|
||||
const tocRegex = /^## Table of contents[\s\S]*?(?=^## )|^## Table of contents[\s\S]*$/gm;
|
||||
const newdata = responseJson.reason.replace(imgRegex, '')
|
||||
.replace(tocRegex, "");
|
||||
const newdata = responseJson.reason.replace(imgRegex, '').replace(tocRegex, "");
|
||||
setData(newdata);
|
||||
if (docId === undefined) {
|
||||
document.title = "Shuffle documentation introduction";
|
||||
@@ -782,88 +785,6 @@ const Docs = (defaultprops) => {
|
||||
fetchDocs(props.match.params.key);
|
||||
}
|
||||
|
||||
// const parseElementScroll = () => {
|
||||
// const offset = 45;
|
||||
// var parent = document.getElementById("markdown_wrapper_outer");
|
||||
// if (parent !== null) {
|
||||
// //console.log("IN PARENT")
|
||||
// var elements = parent.getElementsByTagName("h2");
|
||||
//
|
||||
// const name = window.location.hash
|
||||
// .slice(1, window.location.hash.length)
|
||||
// .toLowerCase()
|
||||
// .split("%20")
|
||||
// .join(" ")
|
||||
// .split("_")
|
||||
// .join(" ")
|
||||
// .split("-")
|
||||
// .join(" ")
|
||||
// .split("?")[0]
|
||||
//
|
||||
// //console.log(name)
|
||||
// var found = false;
|
||||
// for (var key in elements) {
|
||||
// const element = elements[key];
|
||||
// if (element.innerHTML === undefined) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // Fix location..
|
||||
// if (element.innerHTML.toLowerCase() === name) {
|
||||
// //console.log(element.offsetTop)
|
||||
// element.scrollIntoView({ behavior: "smooth" });
|
||||
// //element.scrollTo({
|
||||
// // top: element.offsetTop+offset,
|
||||
// // behavior: "smooth"
|
||||
// //})
|
||||
// found = true;
|
||||
// //element.scrollTo({
|
||||
// // top: element.offsetTop-100,
|
||||
// // behavior: "smooth"
|
||||
// //})
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // H#
|
||||
// if (!found) {
|
||||
// elements = parent.getElementsByTagName("h3");
|
||||
// //console.log("NAMe: ", name)
|
||||
// found = false;
|
||||
// for (key in elements) {
|
||||
// const element = elements[key];
|
||||
// if (element.innerHTML === undefined) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // Fix location..
|
||||
// if (element.innerHTML.toLowerCase() === name) {
|
||||
// element.scrollIntoView({ behavior: "smooth" });
|
||||
// //element.scrollTo({
|
||||
// // top: element.offsetTop-offset,
|
||||
// // behavior: "smooth"
|
||||
// //})
|
||||
// found = true;
|
||||
// //element.scrollTo({
|
||||
// // top: element.offsetTop-100,
|
||||
// // behavior: "smooth"
|
||||
// //})
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// //console.log(element)
|
||||
//
|
||||
// //console.log("NAME: ", name)
|
||||
// //console.log(document.body.innerHTML)
|
||||
// // parent = document.getElementById(parent);
|
||||
//
|
||||
// //var descendants = parent.getElementsByTagName(tagname);
|
||||
//
|
||||
// // this.scrollDiv.current.scrollIntoView({ behavior: 'smooth' });
|
||||
//
|
||||
// //$(".parent").find("h2:contains('Statistics')").parent();
|
||||
// };
|
||||
|
||||
const markdownStyle = {
|
||||
color: "rgba(255, 255, 255, 0.90)",
|
||||
overflow: "hidden",
|
||||
@@ -872,7 +793,7 @@ const Docs = (defaultprops) => {
|
||||
maxWidth: "100%",
|
||||
minWidth: "100%",
|
||||
overflow: "hidden",
|
||||
fontSize: isMobile ? "1.3rem" : "1.1rem",
|
||||
fontSize: isMobile ? "1.3rem" : "0.8rem",
|
||||
};
|
||||
|
||||
const alertNote = {
|
||||
@@ -1084,25 +1005,23 @@ const Docs = (defaultprops) => {
|
||||
<div style={IndexBar}>
|
||||
{tocLines.length > 0 ?
|
||||
(
|
||||
<h4 style={{ fontWeight: 600, margin: 0, fontSize: "16px", marginBottom: "8px" }}>Table Of Content</h4>
|
||||
<h4 style={{ fontWeight: 600, margin: 0, fontSize: "16px", marginBottom: "8px" }}>Table of Content</h4>
|
||||
|
||||
) : null}
|
||||
<nav>
|
||||
{tocLines.map((data, index) => {
|
||||
return (
|
||||
<div className="toc">
|
||||
<div className="toc"
|
||||
>
|
||||
<ListItemButton
|
||||
key={data.text}
|
||||
href={`#${data.id}`}
|
||||
style={{
|
||||
color: activeId === data.id ? "#f86a3e" : "inherit",
|
||||
textDecoration: "none",
|
||||
fontSize: "14px",
|
||||
fontWeight: 400,
|
||||
padding: "4px 0",
|
||||
paddingLeft: "8px",
|
||||
paddingRight: "8px",
|
||||
padding: "4px 8px 4px 8px",
|
||||
lineHeight: "20px",
|
||||
color: activeId === data.id ? "#f86a3e" : "rgba(255,255,255,0.6)",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
handleCollapse(index)
|
||||
|
||||
@@ -80,25 +80,15 @@ const useStyles = makeStyles((theme) => ({
|
||||
datagrid: {
|
||||
border: 0,
|
||||
"& .MuiDataGrid-columnsContainer": {
|
||||
backgroundColor:
|
||||
theme.palette.type === "light" ? "#fafafa" : theme.palette.inputColor,
|
||||
},
|
||||
"& .MuiDataGrid-iconSeparator": {
|
||||
display: "none",
|
||||
},
|
||||
"& .MuiDataGrid-colCell, .MuiDataGrid-cell": {
|
||||
borderRight: `1px solid ${
|
||||
theme.palette.type === "light" ? "white" : "#303030"
|
||||
}`,
|
||||
},
|
||||
"& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": {
|
||||
borderBottom: `1px solid ${
|
||||
theme.palette.type === "light" ? "#f0f0f0" : "#303030"
|
||||
}`,
|
||||
},
|
||||
"& .MuiDataGrid-cell": {
|
||||
color:
|
||||
theme.palette.type === "light" ? "white" : "rgba(255,255,255,0.65)",
|
||||
},
|
||||
"& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption":
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user