import React, { useState, useEffect, useContext, memo } from "react"; import theme from "../theme.jsx"; import { toast } from 'react-toastify'; import ReactJson from "react-json-view-ssr"; import { Typography, Tooltip, Divider, TextField, Button, Tabs, Tab, Grid, List, ListItem, ListItemText, IconButton, Dialog, DialogTitle, DialogActions, Skeleton, Chip, Checkbox, MenuItem, DialogContent, FormControl, Select, } from "@mui/material"; import { Link as LinkIcon, AutoFixHigh as AutoFixHighIcon, AutoFixNormal as AutoFixNormalIcon, Edit as EditIcon, FileCopy as FileCopyIcon, SelectAll as SelectAllIcon, OpenInNew as OpenInNewIcon, CloudDownload as CloudDownloadIcon, Description as DescriptionIcon, Polymer as PolymerIcon, CheckCircle as CheckCircleIcon, Close as CloseIcon, Apps as AppsIcon, Image as ImageIcon, Delete as DeleteIcon, Cached as CachedIcon, AccessibilityNew as AccessibilityNewIcon, Lock as LockIcon, Eco as EcoIcon, Schedule as ScheduleIcon, Cloud as CloudIcon, Business as BusinessIcon, Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon, Clear as ClearIcon, Add as AddIcon, } from "@mui/icons-material"; import { validateJson, } from "../views/Workflows.jsx"; import { Context } from "../context/ContextApi.jsx"; const scrollStyle1 = { height: 100, width: 225, overflow: "hidden", position: "relative", } const scrollStyle2 = { position: "absolute", top: 0, left: 0, bottom: "-20px", right: "-20px", overflow: "scroll", } const CacheView = memo((props) => { const { globalUrl, userdata, serverside, orgId, isSelectedDataStore, selectedOrganization } = props; const [orgCache, setOrgCache] = React.useState(""); const [listCache, setListCache] = React.useState([]); const [addCache, setAddCache] = React.useState(""); const [editedCache, setEditedCache] = React.useState(""); const [modalOpen, setModalOpen] = React.useState(false); const [key, setKey] = React.useState(""); const [value, setValue] = React.useState(""); const [cacheInput, setCacheInput] = React.useState(""); const [cacheCursor, setCacheCursor] = React.useState(""); const [dataValue, setDataValue] = React.useState({}); 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(""); // Direct category migration from ../components/Files.jsx const [selectAllChecked, setSelectAllChecked] = React.useState(false) const [renderTextBox, setRenderTextBox] = React.useState(false); const [fileCategories, setFileCategories] = React.useState(["default"]); const [selectedCategory, setSelectedCategory] = React.useState("default"); const [selectedFileId, setSelectedFileId] = React.useState(""); const [updateToThisCategory, setUpdateToThisCategory] = useState("") const [showFileCategoryPopup, setShowFileCategoryPopup] = React.useState(false); const [selectedFiles, setSelectedFiles] = useState([]); useEffect(() => { if (orgId?.length > 0) { listOrgCache(orgId, selectedCategory) } }, [orgId]) const handleKeyDown = (event) => { if (event.key === 'Enter') { fileCategories.push(event.target.value); setSelectedCategory(event.target.value); setRenderTextBox(false); } if (event.key === 'Escape'){ // not working for some reasons console.log('escape pressed') setRenderTextBox(false); } } const listOrgCache = (orgId, category) => { const url = `${globalUrl}/api/v1/orgs/${orgId}/list_cache${category !== undefined ? `?category=${category.replaceAll(" ", "%20")}` : ""}` fetch(url, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for list cache :O!"); return; } return response.json(); }) .then((responseJson) => { if (responseJson.success === true) { setListCache(responseJson.keys); setCachedLoaded(true); if (fileCategories.length === 1 && fileCategories[0] === "default") { var newcategories = ["default"] for (var key in responseJson.keys) { if (responseJson.keys[key].category !== undefined && responseJson.keys[key].category !== null && responseJson.keys[key].category !== "" && !fileCategories.includes(responseJson.keys[key].category)) { newcategories.push(responseJson.keys[key].category); } } console.log("CATEGORIES: ", newcategories) setFileCategories(newcategories) } } if (responseJson.cursor !== undefined && responseJson.cursor !== null && responseJson.cursor !== "") { setCacheCursor(responseJson.cursor); } }) .catch((error) => { toast(error.toString()); }); }; const deleteCache = (orgId, key) => { const method = "POST" const url = `${globalUrl}/api/v1/orgs/${orgId}/delete_cache` const parsed = { "org_id": orgId, "key": key, "category": selectedCategory === "" || selectedCategory === "default" ? "" : selectedCategory, } fetch(url, { method: method, headers: { Accept: "application/json", }, body: JSON.stringify(parsed), credentials: "include", }) .then((response) => { if (response.status === 200) { toast("Successfully deleted Cache"); setTimeout(() => { listOrgCache(orgId, selectedCategory) }, 1000); } else { toast("Failed deleting Cache. Does it still exist?"); } }) .catch((error) => { toast(error.toString()); }); }; const editOrgCache = (orgId) => { const cache = { key: dataValue.key, value: value, category: selectedCategory, } setCacheInput([cache]); fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", body: JSON.stringify(cache), }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for Cache :O!"); return; } return response.json(); }) .then((responseJson) => { setAddCache(responseJson); toast("Cache Edited Successfully!"); listOrgCache(orgId, selectedCategory); setModalOpen(false); }) .catch((error) => { toast(error.toString()); }); }; const addOrgCache = (orgId) => { const cache = { key: key, value: value, category: selectedCategory, } setCacheInput([cache]); fetch(globalUrl + `/api/v1/orgs/${orgId}/set_cache`, { method: "POST", body: JSON.stringify(cache), headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for apps :O!"); return; } return response.json(); }) .then((responseJson) => { setAddCache(responseJson); toast("New key added Successfully!"); listOrgCache(orgId, selectedCategory); setModalOpen(false); }) .catch((error) => { toast(error.toString()); }); }; const isValidJson = validateJson(value) const autoFixJson = (inputvalue) => { console.log("inputvalue: ", inputvalue) try { var parsedjson = JSON.parse(inputvalue) // setValue() with the parsed json as string setValue(JSON.stringify(parsedjson, null, 2)) } catch (e) { console.log("Error parsing JSON: ", e) toast.info("Invalid JSON.", { autoClose: 1500, }) } } const handleReactJsonClipboard = (copy) => { const elementName = "copy_element_shuffle"; let copyText = document.getElementById(elementName); if (copyText) { if (copy.namespace && copy.name && copy.src) { copy = copy.src; } const clipboard = navigator.clipboard; if (!clipboard) { toast("Can only copy over HTTPS (port 3443)"); return; } let stringified = JSON.stringify(copy); if (stringified.startsWith('"') && stringified.endsWith('"')) { stringified = stringified.slice(1, -1); } navigator.clipboard.writeText(stringified); toast("Copied value to clipboard, NOT json path."); } else { console.log("Failed to copy from " + elementName + ": ", copyText); } }; const modalView = ( // console.log("key:", dataValue.key), //console.log("value:",dataValue.value), { setModalOpen(false); }} PaperProps={{ sx: { borderRadius: theme?.palette?.DialogStyle?.borderRadius, border: theme?.palette?.DialogStyle?.border, minWidth: "800px", minHeight: "320px", fontFamily: theme?.typography?.fontFamily, backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, zIndex: 1000, '& .MuiDialogContent-root': { backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, }, '& .MuiDialogTitle-root': { backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, }, '& .MuiDialogActions-root': { backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, }, }, }} > { editCache ? "Edit Key" : "Add Key"}{selectedCategory === "" || selectedCategory === "default" ? "" : ` in category '${selectedCategory}'`}
Key setKey(e.target.value)} />
Value - ({isValidJson.valid === true ? "Valid" : "Invalid"} JSON) { autoFixJson(value) }} >
setValue(e.target.value)} />
); 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)], selectedCategory) } const editFileConfig = (id, selectedSubOrg, category) => { const data = { Key: id, action: "suborg_distribute", selected_suborgs: selectedSubOrg, category: category === undefined || category === "" || category === "default" ? "" : category, } 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, selectedCategory); setShowDistributionPopup(false); }, 1000); } }) ) .catch((error) => { toast("Err: " + error.toString()); }); }; const cacheDistributionModal = showDistributionPopup ? ( 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, }, }, }} >
Select sub-org to distribute Datastore key
{handleSelectSubOrg(null, "none")}}>None {handleSelectSubOrg(null, "all")}}>All {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 === "" ? ( {data.name} ) : ( {data.name} ); return ( handleSelectSubOrg(data.id)} style={{ display: "flex", alignItems: "center" }} > {image} {data.name} ); })}
) : null; return (
{modalView} {cacheDistributionModal}

Shuffle Datastore {selectedCategory === "" || selectedCategory === "default" ? "" : `- Category '${selectedCategory}'`}

Datastore is a permanent key-value database for storing data that can be used cross-workflow.
You can store anything from lists of IPs to complex configurations.  Learn more
{fileCategories !== undefined && fileCategories !== null && fileCategories.length > 1 ? ( { setShowFileCategoryPopup(false) }} > File Categories Please note that your selected files ({selectedFileId?.length}) will be moved to the {updateToThisCategory} category. ) : null}
{renderTextBox ? : } {renderTextBox && { handleKeyDown(event); if(event.key === 'Enter' && selectedFileId.length > 0){ //setShowFileCategoryPopup(true) setUpdateToThisCategory(event.target.value) } }} style={{ height: 35, width: 200, marginTop: 0, }} InputProps={{ style: { color: "white", height: 35, fontSize: 16, borderRadius: 4, paddingTop: 0, }, }} color="primary" placeholder="Category name" required margin="dense" defaultValue={""} autoFocus />}
{isSelectedDataStore? null :}
{["Key", "Value", "Actions", "Updated", "Distribution"].map((header, index) => ( ))} {cachedLoaded === false ? [...Array(6)].map((_, rowIndex) => ( {Array(5) .fill() .map((_, colIndex) => ( ))} )) : listCache?.length === 0 ? ( {Array(5).fill().map((_, index) => ( ))} ): listCache?.map((data, index) => { var category = selectedCategory if (selectedCategory === "default") { category = "" } if (data?.category === undefined && category === "") { } else if (data?.category !== category) { return null } var bgColor = isSelectedDataStore? "#212121":"#27292d"; if (index % 2 === 0) { bgColor = isSelectedDataStore? "#1A1A1A":"#1f2023"; } const validate = validateJson(data.value); const isDistributed = data?.suborg_distribution?.length > 0 ? true : false; return ( { // handleReactJsonClipboard(copy); }} collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} iconStyle={theme.palette.jsonIconStyle} displayDataTypes={false} onSelect={(select) => { // HandleJsonCopy(showResult, select, data.action.label); console.log("SELECTED!: ", select); }} name={"value"} /> : data.value } /> { setEditCache(true) setDataValue({ "key": data.key, "value":data.value }) setValue(data.value) setModalOpen(true) }} > edit { window.open(`${globalUrl}/api/v1/orgs/${orgId}/cache/${data.key}?type=text&authorization=${data.public_authorization}`, "_blank"); }} > { deleteCache(orgId, data.key); //deleteFile(orgId); }} > )} /> {selectedOrganization.id !== undefined && data?.org_id !== selectedOrganization.id ? } style={{display: "table-cell", textAlign: 'center', verticalAlign: 'middle', }} /> : { setShowDistributionPopup(true) if(data?.suborg_distribution?.length > 0){ setSelectedSubOrg(data.suborg_distribution) }else{ setSelectedSubOrg([]) } setSelectedCacheKey(data.key) }} /> } style={{display: "table-cell", textAlign: 'center', verticalAlign: 'middle', }} /> } ); })}
); }); export default memo(CacheView);