import React, { useState, useEffect } from "react";
import { makeStyles } from "@material-ui/styles";
import { useTheme } from "@material-ui/core/styles";
import { useNavigate, Link } from "react-router-dom";
import countries from "../components/Countries.jsx";
import CodeEditor from "../components/ShuffleCodeEditor.jsx";
import getLocalCodeData from "../components/ShuffleCodeEditor.jsx";
import AddIcon from "@mui/icons-material/Add";
import ClearIcon from '@mui/icons-material/Clear';
//import ToggleButton from '@mui/material/ToggleButton';
import {
FormControl,
InputLabel,
Paper,
OutlinedInput,
Checkbox,
Card,
Tooltip,
FormControlLabel,
Typography,
Switch,
Select,
MenuItem,
Divider,
TextField,
Button,
Tabs,
Tab,
Grid,
List,
ListItem,
ListItemText,
ListItemAvatar,
ListItemSecondaryAction,
IconButton,
Avatar,
Zoom,
Dialog,
DialogTitle,
DialogActions,
DialogContent,
CircularProgress,
Box,
} from "@material-ui/core";
import { Autocomplete } from "@mui/material";
import {
Edit as EditIcon,
FileCopy as FileCopyIcon,
Publish as PublishIcon,
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,
} from "@material-ui/icons";
import { useAlert } from "react-alert";
import Dropzone from "../components/Dropzone";
import HandlePayment from "./HandlePayment";
import OrgHeader from "../components/OrgHeader.jsx";
import { display, style } from "@mui/system";
const useStyles = makeStyles({
notchedOutline: {
borderColor: "#f85a3e !important",
},
});
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 500,
},
},
getContentAnchorEl: () => null,
};
const FileCategoryInput = (props) => {
const isSet = props.isSet;
console.log("inside filecategoryinput");
console.log("isset value" , isSet);
if (isSet){
return (
)}
}
const Admin = (props) => {
const { globalUrl, userdata, serverside } = props;
var upload = "";
var to_be_copied = "";
const theme = useTheme();
const classes = useStyles();
let navigate = useNavigate();
const [firstRequest, setFirstRequest] = React.useState(true);
const [orgRequest, setOrgRequest] = React.useState(true);
const [modalUser, setModalUser] = React.useState({});
const [orgName, setOrgName] = React.useState("");
const [modalOpen, setModalOpen] = React.useState(false);
const [cloudSyncModalOpen, setCloudSyncModalOpen] = React.useState(false);
const [cloudSyncApikey, setCloudSyncApikey] = React.useState("");
const [loading, setLoading] = React.useState(false);
const [selectedOrganization, setSelectedOrganization] = React.useState({});
const [selectedDealModalOpen, setSelectedDealModalOpen] =
React.useState(false);
//console.log("Selected: ", selectedOrganization)
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
const [loginInfo, setLoginInfo] = React.useState("");
const [curTab, setCurTab] = React.useState(0);
const [users, setUsers] = React.useState([]);
const [organizations, setOrganizations] = React.useState([]);
const [orgSyncResponse, setOrgSyncResponse] = React.useState("");
const [userSettings, setUserSettings] = React.useState({});
const [matchingOrganizations, setMatchingOrganizations] = React.useState([]);
const [environments, setEnvironments] = React.useState([]);
const [authentication, setAuthentication] = React.useState([]);
const [schedules, setSchedules] = React.useState([]);
const [files, setFiles] = React.useState([]);
const [selectedNamespace, setSelectedNamespace] = React.useState("default");
const [fileNamespaces, setFileNamespaces] = React.useState([]);
const [selectedUser, setSelectedUser] = React.useState({});
const [newUsername, setNewUsername] = React.useState("");
const [newPassword, setNewPassword] = React.useState("");
const [selectedUserModalOpen, setSelectedUserModalOpen] =
React.useState(false);
const [selectedAuthentication, setSelectedAuthentication] = React.useState(
{}
);
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] =
React.useState(false);
const [authenticationFields, setAuthenticationFields] = React.useState([]);
const [showArchived, setShowArchived] = React.useState(false);
const [isDropzone, setIsDropzone] = React.useState(false);
const [image2FA, setImage2FA] = React.useState("");
const [value2FA, setValue2FA] = React.useState("");
const [secret2FA, setSecret2FA] = React.useState("");
const [show2faSetup, setShow2faSetup] = useState(false);
const [dealName, setDealName] = React.useState("");
const [dealAddress, setDealAddress] = React.useState("");
const [dealType, setDealType] = React.useState("MSSP");
const [dealCountry, setDealCountry] = React.useState("United States");
const [dealCurrency, setDealCurrency] = React.useState("USD");
const [dealStatus, setDealStatus] = React.useState("initiated");
const [dealValue, setDealValue] = React.useState("");
const [dealDiscount, setDealDiscount] = React.useState("");
const [dealerror, setDealerror] = React.useState("");
const [dealList, setDealList] = React.useState([]);
const [fileContent, setFileContent] = React.useState("");
useEffect(() => {
if (isDropzone) {
//redirectOpenApi();
setIsDropzone(false);
}
}, [isDropzone]);
const isCloud =
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io";
const [openEditor, setOpenEditor] = React.useState(false);
const [renderTextBox, setRenderTextBox] = React.useState(false);
const [openFileId, setOpenFileId] = React.useState(false);
const allowedFileTypes = ["txt", "py", "yaml","yml","json"]
const runUpdateText = (text) =>{
fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body:text,
credentials: "include",
}).then((response) => {
if (response.status !== 200) {
console.log("Can't update file");
}
return response.json();
})
//console.log(text);
}
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
console.log('do validate')
console.log("new namespace name->",event.target.value);
fileNamespaces.push(event.target.value);
setSelectedNamespace(event.target.value);
setRenderTextBox(false);
}
if (event.key === 'Escape'){ // not working for some reasons
console.log('escape pressed')
setRenderTextBox(false);
}
}
const get2faCode = (userId) => {
fetch(`${globalUrl}/api/v1/users/${userId}/get2fa`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
}
return response.json();
})
.then((responseJson) => {
//console.log("RESPONSE: ", responseJson)
if (responseJson.success === true) {
//alert.info(responseJson.reason)
setImage2FA(responseJson.reason);
setSecret2FA(responseJson.extra);
}
})
.catch((error) => {
alert.error(error.toString());
});
};
const getApps = () => {
fetch(globalUrl + "/api/v1/apps", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
}
return response.json();
})
.then((responseJson) => {
console.log("apps: ", responseJson);
//setApps(responseJson)
//setFilteredApps(responseJson)
//if (responseJson.length > 0) {
// setSelectedApp(responseJson[0])
// if (responseJson[0].actions !== null && responseJson[0].actions.length > 0) {
// setSelectedAction(responseJson[0].actions[0])
// } else {
// setSelectedAction({})
// }
//}
})
.catch((error) => {
alert.error(error.toString());
});
};
const categories = [
{
name: "Ticketing",
apps: ["TheHive", "Service-Now", "SecureWorks"],
categories: ["tickets", "ticket", "ticketing"],
},
];
/*
"SIEM",
"Active Directory",
"Firewalls",
"Proxies web",
"SIEM",
"SOAR",
"Mail",
"EDR",
"AV",
"MDM/MAM",
"DNS",
"Ticketing platform",
"TIP",
"Communication",
"DDOS protection",
"VMS",
]
*/
const alert = useAlert();
const deleteAuthentication = (data) => {
alert.info("Deleting auth " + data.label);
// Just use this one?
const url = globalUrl + "/api/v1/apps/authentication/" + data.id;
console.log("URL: ", url);
fetch(url, {
method: "DELETE",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
console.log("RESP: ", responseJson);
if (responseJson["success"] === false) {
alert.error("Failed deleting auth");
} else {
// Need to wait because query in ES is too fast
setTimeout(() => {
getAppAuthentication();
}, 1000);
//alert.success("Successfully deleted authentication!")
}
})
)
.catch((error) => {
console.log("Error in userdata: ", error);
});
};
const deleteSchedule = (data) => {
// FIXME - add some check here ROFL
console.log("INPUT: ", data);
// Just use this one?
const url =
globalUrl +
"/api/v1/workflows/" +
data["workflow_id"] +
"/schedule/" +
data.id;
console.log("URL: ", url);
fetch(url, {
method: "DELETE",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
console.log("RESP: ", responseJson);
if (responseJson["success"] === false) {
alert.error("Failed stopping schedule");
} else {
setTimeout(() => {
getSchedules();
}, 1500);
//alert.success("Successfully stopped schedule!")
}
})
)
.catch((error) => {
console.log("Error in userdata: ", error);
});
};
const handleVerify2FA = (userId, code) => {
const data = {
code: code,
user_id: userId,
};
fetch(`${globalUrl}/api/v1/users/${userId}/set2fa`, {
mode: "cors",
method: "POST",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) => {
if (response.status === 200) {
} else {
//alert.info("Wrong code sent.")
//alert.info("Wrong code sent. Please try again.")
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === true) {
alert.info("Successfully enabled 2fa");
setTimeout(() => {
getUsers();
setImage2FA("");
setValue2FA("");
setSecret2FA("");
setShow2faSetup(false);
setSelectedUserModalOpen(false);
}, 1000);
} else {
alert.info("Wrong code sent. Please try again.");
//alert.error("Failed setting 2fa: ", responseJson.reason)
}
})
.catch((error) => {
alert.info("Wrong code sent. Please try again.");
//alert.error("Err: " + error.toString())
});
};
const handleStopOrgSync = (org_id) => {
if (org_id === undefined || org_id === null) {
alert.error("Couldn't get org " + org_id);
return;
}
const data = {};
const url = globalUrl + "/api/v1/orgs/" + org_id + "/stop_sync";
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) => {
if (response.status === 200) {
console.log("Cloud sync success?");
alert.success("Successfully stopped cloud sync");
} else {
console.log("Cloud sync fail?");
alert.error(
"Failed stopping sync. Try again, and contact support if this persists."
);
}
return response.json();
})
.then((responseJson) => {
setTimeout(() => {
handleGetOrg(org_id);
}, 1000);
})
.catch((error) => {
alert.error("Err: " + error.toString());
});
};
const enableCloudSync = (apikey, organization, disableSync) => {
setOrgSyncResponse("");
const data = {
apikey: apikey,
organization: organization,
disable: disableSync,
};
const url = globalUrl + "/api/v1/cloud/setup";
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) => {
setLoading(false);
if (response.status === 200) {
console.log("Cloud sync success?");
} else {
console.log("Cloud sync fail?");
}
return response.json();
//setTimeout(() => {
//}, 1000)
})
.then((responseJson) => {
console.log("RESP: ", responseJson);
if (
responseJson.success === false &&
responseJson.reason !== undefined
) {
setOrgSyncResponse(responseJson.reason);
alert.error("Failed to handle sync: " + responseJson.reason);
} else if (!responseJson.success) {
alert.error("Failed to handle sync.");
} else {
getOrgs();
if (disableSync) {
alert.success("Successfully disabled sync!");
setOrgSyncResponse("Successfully disabled syncronization");
} else {
alert.success("Cloud Syncronization successfully set up!");
setOrgSyncResponse(
"Successfully started syncronization. Cloud features you now have access to can be seen below."
);
}
selectedOrganization.cloud_sync = !selectedOrganization.cloud_sync;
setSelectedOrganization(selectedOrganization);
setCloudSyncApikey("");
handleGetOrg(userdata.active_org.id);
}
})
.catch((error) => {
setLoading(false);
alert.error("Err: " + error.toString());
});
};
const saveAuthentication = (authentication) => {
const data = authentication;
const url = globalUrl + "/api/v1/apps/authentication";
fetch(url, {
mode: "cors",
method: "PUT",
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) {
alert.error("Failed changing authentication");
} else {
//alert.success("Successfully password!")
setSelectedUserModalOpen(false);
getAppAuthentication();
}
})
)
.catch((error) => {
alert.error("Err: " + error.toString());
});
};
const editAuthenticationConfig = (id) => {
const data = {
id: id,
action: "assign_everywhere",
};
const url = globalUrl + "/api/v1/apps/authentication/" + id + "/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) {
alert.error("Failed overwriting appauth in workflows");
} else {
alert.success("Successfully updated auth everywhere!");
setSelectedUserModalOpen(false);
setTimeout(() => {
getAppAuthentication();
}, 1000);
}
})
)
.catch((error) => {
alert.error("Err: " + error.toString());
});
};
const createSubOrg = (currentOrgId, name) => {
const data = { name: name, org_id: currentOrgId };
console.log(data);
const url = globalUrl + `/api/v1/orgs/${currentOrgId}/create_sub_org`;
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) {
if (responseJson.reason !== undefined) {
alert.error(responseJson.reason);
} else {
alert.error("Failed creating suborg. Please try again");
}
} else {
alert.success(
"Successfully created suborg. Reloading in 3 seconds!"
);
setSelectedUserModalOpen(false);
setTimeout(() => {
window.location.reload();
}, 2500);
}
setOrgName("");
setModalOpen(false);
})
)
.catch((error) => {
alert.error("Err: " + error.toString());
});
};
const onPasswordChange = () => {
const data = { username: selectedUser.username, newpassword: newPassword };
const url = globalUrl + "/api/v1/users/passwordchange";
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) {
if (responseJson.reason !== undefined) {
alert.error(responseJson.reason);
} else {
alert.error("Failed setting new password");
}
} else {
alert.success("Successfully updated password!");
setSelectedUserModalOpen(false);
}
})
)
.catch((error) => {
alert.error("Err: " + error.toString());
});
};
const deleteUser = (data) => {
// Just use this one?
const userId = data.id;
const url = globalUrl + "/api/v1/users/" + userId;
fetch(url, {
method: "DELETE",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status === 200) {
getUsers();
}
return response.json();
})
.then((responseJson) => {
if (!responseJson.success && responseJson.reason !== undefined) {
alert.error("Failed to deactivate user: " + responseJson.reason);
} else {
alert.success("Changed activation for user " + data.id);
}
})
.catch((error) => {
console.log("Error in userdata: ", error);
});
};
const handleGetDeals = (orgId) => {
console.log("Get deals!");
if (orgId.length === 0) {
alert.error(
"Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."
);
return;
}
const url = `${globalUrl}/api/v1/orgs/${orgId}/deals`;
fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status !== 200) {
console.log("Bad status code in get deals: ", response.status);
}
return response.json();
})
.then((responseJson) => {
console.log("Got deals: ", responseJson);
if (responseJson.success === false) {
alert.error("Failed loading deals. Contact support if this persists");
} else {
setDealList(responseJson);
}
})
.catch((error) => {
console.log("Error getting org deals: ", error);
alert.error(
"Failed getting deals for your org. Contact support if this persists."
);
});
};
const handleGetOrg = (orgId) => {
if (orgId.length === 0) {
alert.error(
"Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."
);
return;
}
// Just use this one?
var baseurl = globalUrl;
const url = baseurl + "/api/v1/orgs/" + orgId;
fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status === 401) {
}
return response.json();
})
.then((responseJson) => {
if (responseJson["success"] === false) {
alert.error("Failed getting your org: ", responseJson.readon);
} else {
if (
responseJson.sync_features === undefined ||
responseJson.sync_features === null
) {
responseJson.sync_features = {};
}
if (
isCloud &&
responseJson.partner_info !== undefined &&
responseJson.partner_info.reseller === true
) {
handleGetDeals(orgId);
}
setSelectedOrganization(responseJson);
var lists = {
active: {
triggers: [],
features: [],
sync: [],
},
inactive: {
triggers: [],
features: [],
sync: [],
},
};
// FIXME: Set up features
//Object.keys(responseJson.sync_features).map(function(key, index) {
// //console.log(responseJson.sync_features[key])
//})
//setOrgName(responseJson.name)
//setOrgDescription(responseJson.description)
setOrganizationFeatures(lists);
}
})
.catch((error) => {
console.log("Error getting org: ", error);
alert.error("Error getting current organization");
});
};
const inviteUser = (data) => {
//console.log("INPUT: ", data);
setLoginInfo("");
// Just use this one?
var data = {
username: data.Username,
type: "invite",
org_id: selectedOrganization.id,
};
var baseurl = globalUrl;
const url = baseurl + "/api/v1/users/register_org";
fetch(url, {
method: "POST",
credentials: "include",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo("Error: " + responseJson.reason);
} else {
setLoginInfo("");
setModalOpen(false);
setTimeout(() => {
getUsers();
}, 1000);
}
})
)
.catch((error) => {
console.log("Error in userdata: ", error);
});
};
const submitUser = (data) => {
console.log("INPUT: ", data);
setLoginInfo("");
// Just use this one?
var data = { username: data.Username, password: data.Password };
var baseurl = globalUrl;
const url = baseurl + "/api/v1/users/register";
fetch(url, {
method: "POST",
credentials: "include",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo("Error: " + responseJson.reason);
} else {
setLoginInfo("");
setModalOpen(false);
setTimeout(() => {
getUsers();
}, 1000);
}
})
)
.catch((error) => {
console.log("Error in userdata: ", error);
});
};
// Horrible frontend fix for environments
const setDefaultEnvironment = (environment) => {
// FIXME - add more checks to this
alert.info("Setting default env to " + environment.name);
var newEnv = [];
for (var key in environments) {
if (environments[key].id == environment.id) {
if (environments[key].archived) {
alert.error("Can't set archived to default");
return;
}
environments[key].default = true;
} else if (
environments[key].default == true &&
environments[key].id !== environment.id
) {
environments[key].default = false;
}
newEnv.push(environments[key]);
}
// Just use this one?
const url = globalUrl + "/api/v1/setenvironments";
fetch(url, {
method: "PUT",
credentials: "include",
body: JSON.stringify(newEnv),
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
alert.error(responseJson.reason);
setTimeout(() => {
getEnvironments();
}, 1500);
} else {
setLoginInfo("");
setModalOpen(false);
setTimeout(() => {
getEnvironments();
}, 1500);
}
})
)
.catch((error) => {
console.log("Error in backend data: ", error);
});
};
const flushQueue = (name) => {
// Just use this one?
const url = globalUrl + "/api/v1/flush_queue";
fetch(url, {
method: "DELETE",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
alert.error(responseJson.reason);
getEnvironments();
} else {
setLoginInfo("");
setModalOpen(false);
getEnvironments();
}
})
)
.catch((error) => {
console.log("Error when deleting: ", error);
});
};
const abortEnvironmentWorkflows = (environment) => {
//console.log("Aborting all workflows started >10 minutes ago, not finished");
fetch(
`${globalUrl}/api/v1/environments/${environment.id}/stop?deleteall=true`,
{
method: "GET",
credentials: "include",
}
)
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
alert.error("Failed aborting dangling workflows");
return;
} else {
alert.info("Aborted all dangling workflows");
}
return response.json();
})
.then((responseJson) => {
console.log("Got response for execution: ", responseJson);
//console.log("RESPONSE: ", responseJson)
//setFiles(responseJson)
})
.catch((error) => {
//alert.error(error.toString())
});
};
const deleteEnvironment = (environment) => {
// FIXME - add some check here ROFL
//const name = environment.name
//alert.info("Modifying environment " + name)
//var newEnv = []
//for (var key in environments) {
// if (environments[key].Name == name) {
// if (environments[key].default) {
// alert.error("Can't modify the default environment")
// return
// }
// if (environments[key].type === "cloud" && !environments[key].archived) {
// alert.error("Can't modify cloud environments")
// return
// }
// environments[key].archived = !environments[key].archived
// }
// newEnv.push(environments[key])
//}
const id = environment.id;
//alert.info("Modifying environment " + environment.Name)
var newEnv = [];
for (var key in environments) {
if (environments[key].id == id) {
if (environments[key].default) {
alert.error("Can't modify the default environment");
return;
}
if (environments[key].type === "cloud" && !environments[key].archived) {
alert.error("Can't modify cloud environments");
return;
}
environments[key].archived = !environments[key].archived;
}
newEnv.push(environments[key]);
}
// Just use this one?
const url = globalUrl + "/api/v1/setenvironments";
fetch(url, {
method: "PUT",
credentials: "include",
body: JSON.stringify(newEnv),
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
alert.error(responseJson.reason);
getEnvironments();
} else {
setLoginInfo("");
setModalOpen(false);
getEnvironments();
}
})
)
.catch((error) => {
console.log("Error when deleting: ", error);
});
};
const submitEnvironment = (data) => {
// FIXME - add some check here ROFL
environments.push({
name: data.environment,
type: "onprem",
});
// Just use this one?
var baseurl = globalUrl;
const url = baseurl + "/api/v1/setenvironments";
fetch(url, {
method: "PUT",
credentials: "include",
body: JSON.stringify(environments),
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo("Error in input: " + responseJson.reason);
getEnvironments();
} else {
setLoginInfo("");
setModalOpen(false);
getEnvironments();
}
})
)
.catch((error) => {
console.log("Error in userdata: ", error);
});
};
const handleFileUpload = (file_id, file) => {
//console.log("FILE: ", file_id, file)
fetch(`${globalUrl}/api/v1/files/${file_id}/upload`, {
method: "POST",
credentials: "include",
body: file,
})
.then((response) => {
if (response.status !== 200 && response.status !== 201) {
console.log("Status not 200 for apps :O!");
alert.error("File was created, but failed to upload.");
return;
}
return response.json();
})
.then((responseJson) => {
//console.log("RESPONSE: ", responseJson)
//setFiles(responseJson)
})
.catch((error) => {
alert.error(error.toString());
});
};
const handleCreateFile = (filename, file) => {
var data = {
filename: filename,
org_id: selectedOrganization.id,
workflow_id: "global",
};
if (
selectedNamespace !== undefined &&
selectedNamespace !== null &&
selectedNamespace.length > 0 &&
selectedNamespace !== "default"
) {
data.namespace = selectedNamespace;
}
fetch(globalUrl + "/api/v1/files/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
body: JSON.stringify(data),
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
//console.log("RESP: ", responseJson)
if (responseJson.success === true) {
handleFileUpload(responseJson.id, file);
} else {
alert.error("Failed to upload file ", filename);
}
})
.catch((error) => {
alert.error("Failed to upload file ", filename);
console.log(error.toString());
});
};
const getFiles = () => {
fetch(globalUrl + "/api/v1/files", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson.files !== undefined && responseJson.files !== null) {
setFiles(responseJson.files);
} else {
setFiles([]);
}
console.log("NAMESPACES: ", responseJson.namespaces);
if (
responseJson.namespaces !== undefined &&
responseJson.namespaces !== null
) {
setFileNamespaces(responseJson.namespaces);
}
})
.catch((error) => {
alert.error(error.toString());
});
};
const deleteFile = (file) => {
fetch(globalUrl + "/api/v1/files/" + file.id, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for file delete :O!");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success) {
alert.info("Successfully deleted file " + file.name);
} else if (
responseJson.reason !== undefined &&
responseJson.reason !== null
) {
alert.error("Failed to delete file: " + responseJson.reason);
}
setTimeout(() => {
getFiles();
}, 1500);
console.log(responseJson);
})
.catch((error) => {
alert.error(error.toString());
});
};
const readFileData = (file) => {
fetch(globalUrl + "/api/v1/files/" + file.id + "/content", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for file :O!");
return "";
}
return response.text();
})
.then((respdata) => {
// console.log("respdata ->", respdata);
// console.log("respdata type ->", typeof(respdata));
if (respdata.length === 0) {
alert.error("Failed getting file. Is it deleted?");
return;
}
return respdata
})
.then((responseData) => {
setFileContent(responseData);
//console.log("filecontent state ",fileContent);
})
.catch((error) => {
alert.error(error.toString());
});
};
var localData = "";
// useEffect(() => {
// console.log('confirm', fileContent);
// }, [fileContent])
const downloadFile = (file) => {
fetch(globalUrl + "/api/v1/files/" + file.id + "/content", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return "";
}
return response.text();
})
.then((respdata) => {
if (respdata.length === 0) {
alert.error("Failed getting file. Is it deleted?");
return;
}
var blob = new Blob([respdata], {
type: "application/octet-stream",
});
var url = URL.createObjectURL(blob);
var link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", `${file.filename}`);
var event = document.createEvent("MouseEvents");
event.initMouseEvent(
"click",
true,
true,
window,
1,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null
);
link.dispatchEvent(event);
//return response.json()
})
.then((responseJson) => {
//console.log(responseJson)
//setSchedules(responseJson)
})
.catch((error) => {
alert.error(error.toString());
});
};
const getSchedules = () => {
fetch(globalUrl + "/api/v1/workflows/schedules", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
setSchedules(responseJson);
})
.catch((error) => {
alert.error(error.toString());
});
};
const getAppAuthentication = () => {
fetch(globalUrl + "/api/v1/apps/authentication", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success) {
//console.log(responseJson.data)
//console.log(responseJson)
setAuthentication(responseJson.data);
} else {
alert.error("Failed getting authentications");
}
})
.catch((error) => {
alert.error(error.toString());
});
};
const getEnvironments = () => {
fetch(globalUrl + "/api/v1/getenvironments", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
setEnvironments(responseJson);
})
.catch((error) => {
alert.error(error.toString());
});
};
const getOrgs = () => {
// API no longer in use, as it's in handleInfo request
return;
fetch(globalUrl + "/api/v1/orgs", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
setOrganizations(responseJson);
})
.catch((error) => {
alert.error(error.toString());
});
};
const getUsers = () => {
fetch(globalUrl + "/api/v1/getusers", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
// Ahh, this happens because they're not admin
// window.location.pathname = "/workflows"
return;
}
return response.json();
})
.then((responseJson) => {
setUsers(responseJson);
})
.catch((error) => {
alert.error(error.toString());
});
};
const getSettings = () => {
fetch(globalUrl + "/api/v1/getsettings", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 when getting settings :O!");
}
return response.json();
})
.then((responseJson) => {
setUserSettings(responseJson);
})
.catch((error) => {
console.log(error);
});
};
const views = {
0: "organization",
1: "users",
2: "app_auth",
3: "files",
4: "schedules",
5: "environments",
6: "suborgs",
};
const setConfig = (event, inputValue) => {
const newValue = parseInt(inputValue);
setCurTab(newValue);
if (newValue === 1) {
document.title = "Shuffle - admin - users";
getUsers();
} else if (newValue === 2) {
document.title = "Shuffle - admin - app authentication";
getAppAuthentication();
} else if (newValue === 3) {
document.title = "Shuffle - admin - files";
getFiles();
} else if (newValue === 4) {
document.title = "Shuffle - admin - schedules";
getSchedules();
} else if (newValue === 5) {
document.title = "Shuffle - admin - environments";
getEnvironments();
} else if (newValue === 6) {
document.title = "Shuffle - admin - orgs";
getOrgs();
} else {
document.title = "Shuffle - admin";
}
console.log("NEWVALUE: ", newValue);
if (newValue === 6) {
console.log("Should get apps for categories.");
}
console.log("PROPS: ", props);
navigate(`/admin?tab=${views[newValue]}`);
setModalUser({});
};
if (firstRequest) {
setFirstRequest(false);
document.title = "Shuffle - admin";
if (!isCloud) {
getUsers();
} else {
getSettings();
}
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 foundTab = params["tab"];
if (foundTab !== null && foundTab !== undefined) {
for (var key in Object.keys(views)) {
const value = views[key];
console.log(key, value);
if (value === foundTab) {
setConfig("", key);
break;
}
}
}
}
}
if (
selectedOrganization.id === undefined &&
userdata !== undefined &&
userdata.active_org !== undefined &&
orgRequest
) {
setOrgRequest(false);
handleGetOrg(userdata.active_org.id);
}
const paperStyle = {
maxWidth: 1250,
margin: "auto",
color: "white",
backgroundColor: theme.palette.surfaceColor,
marginBottom: 10,
padding: 20,
};
const changeModalData = (field, value) => {
modalUser[field] = value;
};
const setUser = (userId, field, value) => {
const data = { user_id: userId };
data[field] = value;
fetch(globalUrl + "/api/v1/users/updateuser", {
method: "PUT",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(data),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
} else {
getUsers();
}
return response.json();
})
.then((responseJson) => {
if (!responseJson.success && responseJson.reason !== undefined) {
alert.error("Failed setting user: " + responseJson.reason);
} else {
alert.success("Set the user field " + field + " to " + value);
if (field !== "suborgs") {
setSelectedUserModalOpen(false);
}
}
})
.catch((error) => {
console.log(error);
});
};
const generateApikey = (user) => {
const userId = user.id;
const data = { user_id: userId };
fetch(globalUrl + "/api/v1/generateapikey", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(data),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
} else {
getUsers();
}
return response.json();
})
.then((responseJson) => {
console.log("RESP: ", responseJson);
if (!responseJson.success && responseJson.reason !== undefined) {
alert.error("Failed getting new: " + responseJson.reason);
} else {
alert.success("Got new API key");
}
})
.catch((error) => {
console.log(error);
});
};
const editAuthenticationModal = selectedAuthenticationModalOpen ? (
{
setSelectedAuthenticationModalOpen(false);
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
Edit authentication for {selectedAuthentication.app.name} (
{selectedAuthentication.label})
{selectedAuthentication.fields.map((data, index) => {
//console.log("DATA: ", data, selectedAuthentication)
return (
{data.key}
{
authenticationFields[index].value = e.target.value;
setAuthenticationFields(authenticationFields);
}}
/>
);
})}
setSelectedAuthenticationModalOpen(false)}
color="primary"
>
Cancel
{
var error = false;
for (var key in authenticationFields) {
const item = authenticationFields[key];
if (item.value.length === 0) {
console.log("ITEM: ", item);
//var currentnode = cy.getElementById(data.id)
var textfield = document.getElementById(
`authentication-${key}`
);
if (textfield !== null && textfield !== undefined) {
console.log("HANDLE ERROR FOR KEY ", key);
}
error = true;
}
}
if (error) {
alert.error("All fields must have a new value");
} else {
alert.success("Saving new version of this authentication");
selectedAuthentication.fields = authenticationFields;
saveAuthentication(selectedAuthentication);
setSelectedAuthentication({});
setSelectedAuthenticationModalOpen(false);
}
}}
color="primary"
>
Submit
) : null;
const handleOrgEditChange = (event) => {
if (userdata.id === selectedUser.id) {
alert.info("Can't remove orgs from yourself");
return;
}
console.log("event: ", event.target.value);
setMatchingOrganizations(event.target.value);
// Workaround for empty orgs
if (event.target.value.length === 0) {
event.target.value.push("REMOVE");
}
setUser(selectedUser.id, "suborgs", event.target.value);
//setUser(selectedUser.id, "suborgs", matchingOrganizations)
};
const userOrgEdit =
selectedUser.id !== undefined &&
selectedUser.orgs !== undefined &&
selectedUser.orgs !== null &&
selectedOrganization.child_orgs !== undefined &&
selectedOrganization.child_orgs !== null &&
selectedOrganization.child_orgs.length > 0 ? (
Accessible Sub-Organizations (
{selectedUser.orgs ? selectedUser.orgs.length - 1 : 0})
}
renderValue={(selected) => {
return selected.join(", ");
}}
MenuProps={MenuProps}
>
{selectedOrganization.child_orgs.map((org, index) => (
-1} />
))}
) : null;
const products = [
{ code: "", label: "MSSP", phone: "" },
{ code: "", label: "Enterprise", phone: "" },
{ code: "", label: "Consultancy", phone: "" },
{ code: "", label: "Support", phone: "" },
];
const addDealModal = (
{
setSelectedDealModalOpen(false);
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
Register new deal
{
setDealName(e.target.value);
}}
/>
{
setDealAddress(e.target.value);
}}
/>
{
setDealValue(e.target.value);
}}
/>
option.label}
onChange={(event, newValue) => {
setDealCountry(newValue.label);
}}
renderOption={(props, option) => (
img": { mr: 2, flexShrink: 0 } }}
{...props}
>
{option.label} ({option.code}) +{option.phone}
)}
renderInput={(params) => (
)}
/>
{
setDealType(newValue);
}}
getOptionLabel={(option) => option.label}
renderOption={(props, option) => (
img": { mr: 2, flexShrink: 0 } }}
{...props}
>
{option.label}
)}
renderInput={(params) => (
)}
/>
{dealerror.length > 0 ? (
error registering: {dealerror}
) : null}
{
setSelectedDealModalOpen(false);
//setDealName("")
//setDealAddress("")
//setDealCountry("")
//setDealValue("")
}}
>
Cancel
{
submitDeal(dealName, dealAddress, dealCountry, dealValue);
}}
>
Submit
);
const editUserModal = (
{
setSelectedUserModalOpen(false);
setImage2FA("");
setSecret2FA("");
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
Editing {selectedUser.username}
{isCloud ? null : (
{
setNewUsername(e.target.value);
}}
/>
{
setUser(selectedUser.id, "username", newUsername);
}}
>
Submit
)}
{isCloud ? null : (
setNewPassword(e.target.value)}
/>
onPasswordChange()}
>
Submit
)}
{userOrgEdit}
{
deleteUser(selectedUser);
setSelectedUserModalOpen(false);
}}
>
{selectedUser.active ? "Delete from org" : "Delete from org"}
generateApikey(selectedUser)}
>
Renew API-key
{
run2FASetup(userdata);
}}
disabled={
selectedUser.role === "admin" &&
selectedUser.username !== userdata.username
}
variant="outlined"
color="primary"
>
{selectedUser.mfa_info !== undefined &&
selectedUser.mfa_info !== null &&
selectedUser.mfa_info.active === true
? "Disable 2FA"
: "Enable 2FA"}
{show2faSetup ? (
{/*
*/}
{secret2FA !== undefined &&
secret2FA !== null &&
secret2FA.length > 0 ? (
Scan the image below with the two-factor authentication app on
your phone. If you can’t use a QR code, use the code{" "}
{secret2FA} instead.
) : null}
{image2FA !== undefined &&
image2FA !== null &&
image2FA.length > 0 ? (
) : (
)}
After scanning the QR code image, the app will display a code that
you can enter below.
{
if (event.target.value.length > 6) {
return;
}
setValue2FA(event.target.value);
}}
/>
{
handleVerify2FA(userdata.id, value2FA);
}}
color="primary"
>
Submit
) : null}
);
const GridItem = (props) => {
const [expanded, setExpanded] = React.useState(false);
const primary = props.data.primary;
const secondary = props.data.secondary;
const primaryIcon = props.data.icon;
const secondaryIcon = props.data.active ? (
) : (
);
return (
{
setExpanded(!expanded);
}}
>
{primaryIcon}
{secondaryIcon}
{expanded ? (
Usage:{" "}
{props.data.limit === 0 ? (
"Infinite"
) : (
{props.data.usage} / {props.data.limit}
)}
Data sharing: {props.data.data_collection}
Description: {secondary}
) : null}
);
};
const itemColor = "black";
var syncList = [
{
primary: "Workflows",
secondary: "",
active: true,
icon: ,
},
{
primary: "Apps",
secondary: "",
active: true,
icon: ,
},
{
primary: "Organization",
secondary: "",
active: true,
icon: ,
},
];
const cloudSyncModal = (
{
setCloudSyncModalOpen(false);
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
Enable cloud features
What does{" "}
cloud sync
{" "}
do?
{
setCloudSyncApikey(event.target.value);
}}
/>
{
setLoading(true);
enableCloudSync(
cloudSyncApikey,
selectedOrganization,
selectedOrganization.cloud_sync
);
}}
color="primary"
variant={
selectedOrganization.cloud_sync === true
? "outlined"
: "contained"
}
>
{selectedOrganization.cloud_sync ? "Stop sync" : "Start sync"}
{orgSyncResponse.length > 0 ? (
Error: {orgSyncResponse}
) : null}
{syncList.map((data, index) => {
return ;
})}
* New triggers (userinput, hotmail realtime)
* Execute in the cloud rather than onprem
* Apps can be built in the cloud
* Easily share apps and workflows
* Access to powerful cloud search
);
const submitDeal = (dealName, dealAddress, dealCountry, dealValue) => {
if (dealerror.length > 0) {
setDealerror("");
}
const orgId = selectedOrganization.id;
const data = {
reseller_org: orgId,
name: dealName,
address: dealAddress,
country: dealCountry,
value: dealValue,
};
const url = `${globalUrl}/api/v1/orgs/${orgId}/deals`;
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(function (response) {
if (response.status !== 200) {
console.log("Error in response");
}
return response.json();
})
.then(function (responseJson) {
if (responseJson.success === true) {
setSelectedDealModalOpen(false);
alert.success(
"Added new deal! We will be in touch shortly with an update."
);
setDealName("");
setDealAddress("");
setDealValue("");
setDealCountry("United States");
setDealType("MSSP");
} else {
setDealerror(responseJson.reason);
}
})
.catch(function (error) {
//console.log("Error: ", error);
setDealerror(error.toString());
alert.error("Failed adding deal reg: ", error);
});
};
const cancelSubscriptions = (subscription_id) => {
console.log(selectedOrganization);
const orgId = selectedOrganization.id;
const data = {
subscription_id: subscription_id,
action: "cancel",
org_id: selectedOrganization.id,
};
const url = globalUrl + `/api/v1/orgs/${orgId}`;
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(function (response) {
if (response.status !== 200) {
console.log("Error in response");
}
handleGetOrg(selectedOrganization.id);
return response.json();
})
.then(function (responseJson) {
if (responseJson.success !== undefined && responseJson.success) {
alert.success("Successfully stopped subscription!");
} else {
alert.error("Failed stopping subscription. Please contact us.");
}
})
.catch(function (error) {
console.log("Error: ", error);
alert.error("Failed stopping subscription. Please contact us.");
});
};
const organizationView =
curTab === 0 && selectedOrganization.id !== undefined ? (
Organization overview
On this page you can configure individual parts of your
organization.{" "}
Learn more
{selectedOrganization.id === undefined ? (
Loading Organization
) : (
{
const elementName = "copy_element_shuffle";
const org_id = selectedOrganization.id;
var copyText = document.getElementById(elementName);
if (copyText !== null && copyText !== undefined) {
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
alert.error("Can only copy over HTTPS (port 3443)");
return;
}
navigator.clipboard.writeText(org_id);
copyText.select();
copyText.setSelectionRange(
0,
99999
); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
alert.info(org_id + " copied to clipboard");
}
}}
>
{selectedOrganization.name.length > 0 ? (
) : (
Loading Organization
)}
Cloud syncronization
What does{" "}
cloud sync
{" "}
do? Cloud syncronization is a way of getting more out of Shuffle.
Shuffle will
ALWAYS make every option open source, but
features relying on other users can't be done without a
collaborative approach.
{isCloud ? (
Currently syncronizing:{" "}
{selectedOrganization.cloud_sync_active === true
? "True"
: "False"}
{selectedOrganization.cloud_sync_active ? (
Syncronization interval:{" "}
{selectedOrganization.sync_config.interval === 0
? "60"
: selectedOrganization.sync_config.interval}
) : null}
Your Apikey
{selectedOrganization.cloud_sync_active ? (
{
handleStopOrgSync(selectedOrganization.id);
}}
>
Stop Sync
) : null}
) : (
{
setCloudSyncApikey(event.target.value);
}}
/>
{
setLoading(true);
enableCloudSync(
cloudSyncApikey,
selectedOrganization,
selectedOrganization.cloud_sync
);
}}
color="primary"
variant={
selectedOrganization.cloud_sync === true
? "outlined"
: "contained"
}
>
{selectedOrganization.cloud_sync
? "Stop sync"
: "Start sync"}
{orgSyncResponse.length > 0 ? (
Message from Shuffle Cloud: {orgSyncResponse}
) : null}
)}
Cloud sync features
{selectedOrganization.sync_features === undefined ||
selectedOrganization.sync_features === null
? null
: Object.keys(selectedOrganization.sync_features).map(function (
key,
index
) {
if (key === "schedule") {
return null;
}
const item = selectedOrganization.sync_features[key];
const newkey = key.replaceAll("_", " ");
const griditem = {
primary: newkey,
secondary:
item.description === undefined ||
item.description === null ||
item.description.length === 0
? "Not defined yet"
: item.description,
limit: item.limit,
usage: 0,
data_collection: "None",
active: item.active,
icon: ,
};
return (
);
})}
{isCloud &&
selectedOrganization.partner_info !== undefined &&
selectedOrganization.partner_info.reseller === true ? (
Reseller dashboard
{
setSelectedDealModalOpen(true);
}}
>
Add deal
{
handleGetDeals(userdata.active_org.id);
}}
>
{dealList.length === 0 ? (
No deals registered yet. Click "Add deal" to register one
) : (
dealList.map((deal, index) => {
var bgColor = "#27292d";
if (index % 2 === 0) {
bgColor = "#1f2023";
}
return (
);
})
)}
) : null}
{isCloud &&
selectedOrganization.subscriptions !== undefined &&
selectedOrganization.subscriptions !== null &&
selectedOrganization.subscriptions.length > 0 ? (
Your subscription
{selectedOrganization.subscriptions.length > 1 ? "s" : ""}
{selectedOrganization.subscriptions
.reverse()
.map((sub, index) => {
return (
Type : {sub.level}
Recurrence : {sub.recurrence}
{sub.active ? (
Started :{" "}
{new Date(sub.startdate * 1000).toISOString()}
{
cancelSubscriptions(sub.reference);
}}
>
Cancel subscription
) : (
Cancelled :{" "}
{new Date(
sub.cancellationdate * 1000
).toISOString()}
Status : Deactivated
)}
);
})}
) : null}
)}
) : null;
const modalView = (
{
setModalOpen(false);
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
{curTab === 1
? "Add user"
: curTab === 6
? "Add Sub-Organization"
: "Add environment"}
{curTab === 1 && isCloud ? (
We'll send an email to invite them to your organization.
) : curTab === 6 ? (
The organization created will become a child of your current
organization, and be available to you.
) : null}
{curTab === 1 ? (
Username
changeModalData("Username", event.target.value)
}
/>
{isCloud ? null : (
Password
changeModalData("Password", event.target.value)
}
/>
)}
) : curTab === 6 ? (
Name
{
setOrgName(event.target.value);
}}
/>
) : curTab === 5 ? (
Environment Name
changeModalData("environment", event.target.value)
}
/>
) : null}
{loginInfo}
setModalOpen(false)}
color="primary"
>
Cancel
{
if (curTab === 1) {
if (isCloud) {
inviteUser(modalUser);
} else {
submitUser(modalUser);
}
} else if (curTab === 6) {
createSubOrg(selectedOrganization.id, orgName);
} else if (curTab === 5) {
submitEnvironment(modalUser);
}
}}
color="primary"
>
Submit
);
const usersView =
curTab === 1 ? (
User management
Add, edit, block or change passwords.{" "}
Learn more
setModalOpen(true)}
>
Add user
getUsers()}
>
{selectedOrganization.child_orgs !== undefined &&
selectedOrganization.child_orgs !== null &&
selectedOrganization.child_orgs.length > 0 ? (
) : null}
{users === undefined || users === null
? null
: users.map((data, index) => {
var bgColor = "#27292d";
if (index % 2 === 0) {
bgColor = "#1f2023";
}
return (
{
const elementName = "copy_element_shuffle";
var copyText =
document.getElementById(elementName);
if (
copyText !== null &&
copyText !== undefined
) {
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
alert.error(
"Can only copy over HTTPS (port 3443)"
);
return;
}
navigator.clipboard.writeText(data.apikey);
copyText.select();
copyText.setSelectionRange(
0,
99999
); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
alert.info("Apikey copied to clipboard");
}
}}
>
)
}
/>
{
console.log("VALUE: ", e.target.value);
setUser(data.id, "role", e.target.value);
}}
style={{
backgroundColor: theme.palette.surfaceColor,
color: "white",
height: "50px",
}}
>
Org Admin
Org User
Org Reader
}
style={{ minWidth: 135, maxWidth: 135, marginRight: 15 }}
/>
{selectedOrganization.child_orgs !== undefined &&
selectedOrganization.child_orgs !== null &&
selectedOrganization.child_orgs.length > 0 ? (
) : null}
{
setSelectedUserModalOpen(true);
setSelectedUser(data);
// Find matching orgs between current org and current user's access to those orgs
if (
userdata.orgs !== undefined &&
userdata.orgs !== null &&
userdata.orgs.length > 0 &&
selectedOrganization.child_orgs !== undefined &&
selectedOrganization.child_orgs !== null &&
selectedOrganization.child_orgs.length > 0
) {
console.log("In here?");
var active = [];
for (var key in userdata.orgs) {
console.log("ORG: ", userdata.orgs[key]);
const found =
selectedOrganization.child_orgs.find(
(item) => item.id === userdata.orgs[key].id
);
if (found !== null && found !== undefined) {
if (
data.orgs === undefined ||
data.orgs === null
) {
continue;
}
const subfound = data.orgs.find(
(item) => item === found.id
);
if (
subfound !== null &&
subfound !== undefined
) {
active.push(subfound);
}
}
}
setMatchingOrganizations(active);
}
}}
>
{/* {
generateApikey(data)
}}
disabled={data.role === "admin" && data.username !== userdata.username}
variant="outlined"
color="primary"
>
New apikey
*/}
);
})}
) : null;
const run2FASetup = (data) => {
console.log("2fa: ", data, show2faSetup);
if (!show2faSetup) {
get2faCode(data.id);
} else {
// Should remove?
setImage2FA("");
setSecret2FA("");
}
setShow2faSetup(!show2faSetup);
//setShow2faSetup(true);
};
const uploadFiles = (files) => {
for (var key in files) {
try {
const filename = files[key].name;
var filedata = new FormData();
filedata.append("shuffle_file", files[key]);
if (typeof files[key] === "object") {
handleCreateFile(filename, filedata);
}
/*
reader.addEventListener('load', (e) => {
var data = e.target.result;
setIsDropzone(false)
console.log(filename)
console.log(data)
console.log(files[key])
})
reader.readAsText(files[key])
*/
} catch (e) {
console.log("Error in dropzone: ", e);
}
}
setTimeout(() => {
getFiles();
}, 2500);
};
const uploadFile = (e) => {
const isDropzone =
e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
const files = isDropzone ? e.dataTransfer.files : e.target.files;
//const reader = new FileReader();
//alert.info("Starting fileupload")
uploadFiles(files);
};
const filesView =
curTab === 3 ? (
1366 ? 1366 : 1200,
margin: "auto",
padding: 20,
}}
onDrop={uploadFile}
>
{
upload.click();
}}
>
Upload files
{/*
*/}
(upload = ref)}
onChange={(event) => {
//const file = event.target.value
//const fileObject = URL.createObjectURL(actualFile)
//setFile(fileObject)
//const files = event.target.files[0]
uploadFiles(event.target.files);
}}
/>
getFiles()}
>
{fileNamespaces !== undefined &&
fileNamespaces !== null &&
fileNamespaces.length > 1 ? (
File Category
{
console.log("CHANGE NAMESPACE: ", event.target);
setSelectedNamespace(event.target.value);
}}
>
{fileNamespaces.map((data, index) => {
return (
{data}
);
})}
) : null}
{renderTextBox ?
{
setRenderTextBox(false);
console.log(" close clicked")
}}
>
:
{
setRenderTextBox(true);
}}
>
}
{renderTextBox &&
{
handleKeyDown(event);
}}
InputProps={{
style: {
color: "white",
},
}}
color="primary"
placeholder="File category name"
required
margin="dense"
defaultValue={""}
autoFocus
/>}
{files === undefined || files === null || files.length === 0
? null
: files.map((file, index) => {
if (file.namespace === "") {
file.namespace = "default";
}
if (file.namespace !== selectedNamespace) {
return null;
}
var bgColor = "#27292d";
if (index % 2 === 0) {
bgColor = "#1f2023";
}
return (
) : (
)
}
style={{
minWidth: 100,
maxWidth: 100,
overflow: "hidden",
}}
/>
{
setOpenEditor(true)
setOpenFileId(file.id)
readFileData(file)
}}
>
{
downloadFile(file);
}}
>
{
deleteFile(file);
}}
>
{
const elementName = "copy_element_shuffle";
var copyText =
document.getElementById(elementName);
if (
copyText !== null &&
copyText !== undefined
) {
const clipboard = navigator.clipboard;
if (clipboard === undefined) {
alert.error(
"Can only copy over HTTPS (port 3443)"
);
return;
}
navigator.clipboard.writeText(file.id);
copyText.select();
copyText.setSelectionRange(
0,
99999
); /* For mobile devices */
/* Copy the text inside the text field */
document.execCommand("copy");
alert.info(file.id + " copied to clipboard");
}
}}
>
style={{
minWidth: 250,
maxWidth: 250,
// overflow: "hidden",
}}
/>
);
})}
) : null;
const schedulesView =
curTab === 4 ? (
Schedules
Schedules used in Workflows. Makes locating and control easier.{" "}
Learn more
{schedules === undefined || schedules === null
? null
: schedules.map((schedule, index) => {
var bgColor = "#27292d";
if (index % 2 === 0) {
bgColor = "#1f2023";
}
return (
{schedule.seconds} seconds
)
}
/>
{schedule.workflow_id}
}
/>
deleteSchedule(schedule)}
>
Stop schedule
);
})}
) : null;
const appCategoryView =
curTab === 7 ? (
Categories
Categories are the categories supported by Shuffle, which are mapped
to apps and workflows
{categories.map((data, index) => {
if (data.apps.length === 0) {
return null;
}
return (
{
console.log("Show apps with this category");
}}
>
Find app ({data.apps === null ? 0 : data.apps.length})
);
})}
) : null;
const updateAppAuthentication = (field) => {
setSelectedAuthenticationModalOpen(true);
setSelectedAuthentication(field);
//{selectedAuthentication.fields.map((data, index) => {
var newfields = [];
for (var key in field.fields) {
newfields.push({
key: field.fields[key].key,
value: "",
});
}
setAuthenticationFields(newfields);
};
const authenticationView =
curTab === 2 ? (
{/* */}
{/*
*/}
{authentication === undefined || authentication === null
? null
: authentication.map((data, index) => {
var bgColor = "#27292d";
if (index % 2 === 0) {
bgColor = "#1f2023";
}
//console.log("Auth data: ", data)
if (data.type === "oauth2") {
data.fields = [
{
key: "url",
value: "Secret. Replaced during app execution!",
},
{
key: "client_id",
value: "Secret. Replaced during app execution!",
},
{
key: "client_secret",
value: "Secret. Replaced during app execution!",
},
{
key: "scope",
value: "Secret. Replaced during app execution!",
},
];
}
return (
style={{ minWidth: 75, maxWidth: 75 }}
/>
{/*
*/}
{/*
*/}
{
return data.key;
})
.join(", ")
}
style={{
minWidth: 125,
maxWidth: 125,
overflow: "hidden",
}}
/>
{
updateAppAuthentication(data);
}}
>
{data.defined ? (
{
editAuthenticationConfig(data.id);
}}
>
) : (
{}}
>
)}
{
deleteAuthentication(data);
}}
>
);
})}
) : null;
const environmentView =
curTab === 5 ? (
Environments
Decides what Orborus environment to execute an action in a workflow
in.{" "}
Learn more
setModalOpen(true)}
>
Add environment
getEnvironments()}
>
{
setShowArchived(!showArchived);
}}
/>{" "}
Show disabled
{environments === undefined || environments === null
? null
: environments.map((environment, index) => {
if (!showArchived && environment.archived) {
return null;
}
if (environment.archived === undefined) {
getEnvironments();
return null;
}
var bgColor = "#27292d";
if (index % 2 === 0) {
bgColor = "#1f2023";
}
return (
{environment.default ? null : (
setDefaultEnvironment(environment)}
color="primary"
>
Make default
)}
deleteEnvironment(environment)}
color="primary"
>
{environment.archived ? "Activate" : "Disable"}
{
console.log(
"Should clear executions for: ",
environment
);
abortEnvironmentWorkflows(environment);
}}
color="primary"
>
Clear executions
);
})}
) : null;
const organizationsTab =
curTab === 6 ? (
Organizations
Global admin: control organizations
{
setModalOpen(true);
}}
>
Add suborganization
{userdata.orgs !== undefined &&
userdata.orgs !== null &&
userdata.orgs.length > 0 ? (
{userdata.orgs.map((data, index) => {
const isSelected =
props.userdata.active_org.id === undefined
? "False"
: props.userdata.active_org.id === data.id
? "True"
: "False";
const imagesize = 40;
const imageStyle = {
width: imagesize,
height: imagesize,
pointerEvents: "none",
};
const image =
data.image === "" ? (
) : (
);
var bgColor = "#27292d";
if (index % 2 === 0) {
bgColor = "#1f2023";
}
return (
{
setCloudSyncModalOpen(true);
setSelectedOrganization(data);
console.log("INVERT CLOUD SYNC");
}}
/>
style={{ minWidth: 150, maxWidth: 150 }}
/>
);
})}
) : null}
) : null;
const hybridTab =
curTab === 7 ? (
Hybrid
{
console.log("INVERT");
}}
/>
style={{ minWidth: 150, maxWidth: 150 }}
/>
) : null;
// primary={environment.Registered ? "true" : "false"}
const iconStyle = { marginRight: 10 };
const data = (
Organization
/>
Users
/>
App Authentication
/>
Files
/>
Schedules
/>
Environments
/>
Organizations
/>
{/*window.location.protocol == "http:" && window.location.port === "3000" ? Hybrid/> : null*/}
{/*window.location.protocol === "http:" && window.location.port === "3000" ? Categories/> : null*/}
{organizationView}
{authenticationView}
{usersView}
{environmentView}
{schedulesView}
{filesView}
{hybridTab}
{organizationsTab}
{appCategoryView}
);
return (
{modalView}
{cloudSyncModal}
{editUserModal}
{addDealModal}
{editAuthenticationModal}
{data}
);
};
export default Admin;