From 941630911eb8b1d2c3d7530c4ace2a4750ca2c8a Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Tue, 26 Apr 2022 16:21:11 +0530 Subject: [PATCH 001/160] updated installation guide --- .github/install-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index df1e5a87..cac7b3b2 100644 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -88,7 +88,7 @@ http://localhost:5001 - REST API - requires [>=go1.13](https://golang.org/dl/) export SHUFFLE_OPENSEARCH_URL="http://localhost:9200" export SHUFFLE_ELASTIC=true cd backend/go-app -go run *.go +go run main.go walkoff.go docker.go ``` Large portions of the backend is written in another repository - [shuffle-shared](https://github.com/frikky/shuffle-shared). If you want to update any of this code and test in realtime, we recommend following these steps: From 8bde1e6a427d149bda3e42bfc41f3b16c5562f41 Mon Sep 17 00:00:00 2001 From: frikky Date: Thu, 6 Jul 2023 18:18:46 +0200 Subject: [PATCH 002/160] Nightly on 1.3.0 --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9e66eb53..daf8f7a0 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - image: ghcr.io/shuffle/shuffle-frontend:latest + image: ghcr.io/shuffle/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -15,7 +15,7 @@ services: depends_on: - backend backend: - image: ghcr.io/shuffle/shuffle-backend:latest + image: ghcr.io/shuffle/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} # Here for debugging: @@ -34,7 +34,7 @@ services: - SHUFFLE_FILE_LOCATION=/shuffle-files restart: unless-stopped orborus: - image: ghcr.io/shuffle/shuffle-orborus:latest + image: ghcr.io/shuffle/shuffle-orborus:nightly container_name: shuffle-orborus hostname: shuffle-orborus networks: From 5b72c8d51718a3b8ac812bdba2b539923898d90d Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 12 Jul 2023 01:28:07 +0200 Subject: [PATCH 003/160] Started react 16->18 migration and material-ui->mui migration --- frontend/Dockerfile | 2 +- frontend/build.sh | 2 +- frontend/package.json | 24 +- frontend/run.sh | 12 +- frontend/src/App.jsx | 9 +- frontend/src/components/AlertTemplate.js | 10 +- frontend/src/components/AppFramework.jsx | 2 +- frontend/src/components/AppGrid.jsx | 6 +- frontend/src/components/Appsearch.jsx | 2 +- frontend/src/components/AppsearchPopout.jsx | 2 +- .../src/components/AuthenticationItem.jsx | 2 +- .../src/components/AuthenticationNormal.jsx | 2 +- .../src/components/AuthenticationWindow.jsx | 476 ++++++++++++++++++ frontend/src/components/CacheView.jsx | 3 +- frontend/src/components/CreatorGrid.jsx | 15 +- frontend/src/components/DocsGrid.jsx | 6 +- frontend/src/components/Dropzone.jsx | 4 +- frontend/src/components/EditWorkflow.jsx | 2 +- frontend/src/components/Files.jsx | 2 +- frontend/src/components/Header.jsx | 5 +- frontend/src/components/OrgHeader.jsx | 9 +- frontend/src/components/OrgHeaderexpanded.jsx | 10 +- frontend/src/components/ParsedAction.jsx | 3 +- frontend/src/components/Searchfield.js | 5 +- frontend/src/components/Searchfield.jsx | 5 +- frontend/src/components/ShuffleCodeEditor.jsx | 2 +- frontend/src/components/WorkflowGrid.jsx | 2 +- frontend/src/components/WorkflowPaper.jsx | 2 +- frontend/src/components/WorkflowPaperNew.jsx | 2 +- frontend/src/components/Workflowsearch.jsx | 2 +- frontend/src/index.js | 20 +- frontend/src/views/Admin.jsx | 37 +- frontend/src/views/AngularWorkflow.jsx | 13 +- frontend/src/views/AppCreator.jsx | 5 +- frontend/src/views/Apps.jsx | 2 +- frontend/src/views/Dashboard.jsx | 2 +- frontend/src/views/DashboardViews.jsx | 2 +- frontend/src/views/Docs.jsx | 2 +- frontend/src/views/Faq.jsx | 4 +- frontend/src/views/GettingStarted.jsx | 8 - frontend/src/views/Landingpage.jsx | 8 +- frontend/src/views/LandingpageNew.jsx | 18 +- frontend/src/views/Search.jsx | 6 +- frontend/src/views/Services.jsx | 2 +- frontend/src/views/Workflows.jsx | 4 +- 45 files changed, 624 insertions(+), 139 deletions(-) create mode 100755 frontend/src/components/AuthenticationWindow.jsx diff --git a/frontend/Dockerfile b/frontend/Dockerfile index b2d4f2ce..46d90c13 100755 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,5 +1,5 @@ # Build environment -FROM node:14 as builder +FROM node:18 as builder RUN mkdir /usr/src/app diff --git a/frontend/build.sh b/frontend/build.sh index 9dc0626f..0f536e9d 100755 --- a/frontend/build.sh +++ b/frontend/build.sh @@ -1,3 +1,3 @@ -npm run build +yarn run build rm -rf ../backend/go-app/build cp -r build/ ../backend/go-app/build diff --git a/frontend/package.json b/frontend/package.json index 5082a3b8..2efb1929 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "shuffler", "homepage": "https://shuffler.io", - "version": "1.2.0", + "version": "1.3.0", "private": true, "dependencies": { "@codemirror/commands": "^6.2.2", @@ -10,12 +10,11 @@ "@emotion/styled": "^11.6.0", "@lezer/highlight": "^1.1.3", "@material-ui/core": "^4.5.2", - "@material-ui/icons": "^4.5.1", "@material-ui/lab": "^4.0.0-alpha.58", "@material-ui/styles": "^4.5.2", "@material-ui/utils": "^4.11.2", "@metamask/detect-provider": "^1.2.0", - "@mui/icons-material": "^5.2.1", + "@mui/icons-material": "^5.14.0", "@mui/material": "^5.2.3", "@mui/x-data-grid": "^5.17.11", "@uiw/codemirror-themes": "^4.19.9", @@ -23,7 +22,7 @@ "@use-it/interval": "^1.0.0", "algoliasearch": "^4.13.1", "class-transformer": "^0.4.0", - "create-react-app": "^4.0.3", + "create-react-app": "^5.0.1", "cytoscape": "^3.15.1", "cytoscape-clipboard": "^2.2.1", "cytoscape-cxtmenu": "^3.1.1", @@ -41,11 +40,13 @@ "material-icons": "^0.7.7", "material-icons-react": "^1.0.4", "material-ui-chip-input": "^2.0.0-beta.2", - "material-ui-nested-menu-item": "^1.0.2", "md5-file": "^4.0.0", "mdbreact": "^4.21.1", + "mime": "^3.0.0", "moment": "^2.29.1", - "react": "^16.14.0", + "mui-nested-menu": "^3.2.1", + "process": "^0.11.10", + "react": "^18.2.0", "react-alert": "^5.5.0", "react-alert-template-basic": "^1.0.0", "react-alice-carousel": "^2.6.4", @@ -55,7 +56,7 @@ "react-cookie": "^4.0.1", "react-cytoscapejs": "^1.2.0", "react-device-detect": "^1.9.10", - "react-dom": "^16.14.0", + "react-dom": "^18.2.0", "react-draggable": "^3.3.2", "react-driftjs": "^1.2.2", "react-dropzone": "^10.1.10", @@ -64,12 +65,12 @@ "react-instantsearch-dom": "^6.28.0", "react-json-pretty": "^2.2.0", "react-json-view": "^1.19.1", - "react-markdown": "^4.2.2", + "react-markdown": "^8.0.7", "react-markdown-github": "^3.3.1", "react-powerhooks": "0.0.7", "react-router": "6.2.1", "react-router-dom": "6.2.1", - "react-scripts": "^4.0.1", + "react-scripts": "^5.0.1", "react-shepherd": "^3.3.6", "reactstrap": "^7.1.0", "reaviz": "^12.1.0", @@ -109,6 +110,9 @@ "babel-eslint": "^10.1.0", "prettier": "2.4.1", "promise-window": "^1.2.1", - "webpack": "^4.44.2" + "react-16": "npm:react@16.13.1", + "react-dom-16": "npm:react-dom@16.13.1", + "react-error-overlay": "6.0.9", + "webpack": "^4.46.0" } } diff --git a/frontend/run.sh b/frontend/run.sh index 0ba2beea..9a608216 100755 --- a/frontend/run.sh +++ b/frontend/run.sh @@ -10,9 +10,9 @@ docker tag ghcr.io/frikky/shuffle-frontend:nightly ghcr.io/shuffle/shuffle-front echo "Starting server" # Rerun build locally for it to update :) -#docker run -it \ -# -p 3001:80 \ -# -p 3002:443 \ -# -v $(pwd)/build:/usr/share/nginx/html:ro \ -# --rm \ -# nginx +docker run -it \ + -p 3001:80 \ + -p 3002:443 \ + -v $(pwd)/build:/usr/share/nginx/html:ro \ + --rm \ + ghcr.io/frikky/shuffle-frontend:nightly diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1ab5b924..a70f701e 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -7,7 +7,7 @@ import { removeCookies, useCookies } from "react-cookie"; import Workflows from "./views/Workflows"; import GettingStarted from "./views/GettingStarted"; import EditWebhook from "./views/EditWebhook"; -import AngularWorkflow from "./views/AngularWorkflow"; +import AngularWorkflow from "./views/AngularWorkflow.jsx"; import Header from "./components/Header.jsx"; import theme from "./theme"; @@ -19,7 +19,7 @@ import Dashboard from "./views/Dashboard.jsx"; import DashboardView from "./views/DashboardViews.jsx"; import AdminSetup from "./views/AdminSetup"; import Admin from "./views/Admin"; -import Docs from "./views/Docs"; +import Docs from "./views/Docs.jsx"; import Introduction from "./views/Introduction"; import SetAuthentication from "./views/SetAuthentication"; import SetAuthenticationSSO from "./views/SetAuthenticationSSO"; @@ -31,10 +31,9 @@ import LoginPage from "./views/LoginPage"; import SettingsPage from "./views/SettingsPage"; import KeepAlive from "./views/KeepAlive.jsx"; -import MyView from "./views/MyView"; - import { createMuiTheme, MuiThemeProvider } from "@material-ui/core/styles"; +import UpdateAuthentication from "./views/UpdateAuthentication.jsx"; import FrameworkWrapper from "./views/FrameworkWrapper.jsx"; import ScrollToTop from "./components/ScrollToTop"; import AlertTemplate from "./components/AlertTemplate"; @@ -472,7 +471,7 @@ const App = (message, props) => { /> } /> - } /> + } /> { const { diff --git a/frontend/src/components/AuthenticationItem.jsx b/frontend/src/components/AuthenticationItem.jsx index eef9806c..c8add063 100644 --- a/frontend/src/components/AuthenticationItem.jsx +++ b/frontend/src/components/AuthenticationItem.jsx @@ -25,7 +25,7 @@ import { Edit as EditIcon, Delete as DeleteIcon, SelectAll as SelectAllIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const AuthenticationItem = (props) => { const { data, index, globalUrl, getAppAuthentication } = props diff --git a/frontend/src/components/AuthenticationNormal.jsx b/frontend/src/components/AuthenticationNormal.jsx index 5d3a4fd3..d6cd8e35 100644 --- a/frontend/src/components/AuthenticationNormal.jsx +++ b/frontend/src/components/AuthenticationNormal.jsx @@ -17,7 +17,7 @@ import { import { LockOpen as LockOpenIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const AuthenticationData = (props) => { const { diff --git a/frontend/src/components/AuthenticationWindow.jsx b/frontend/src/components/AuthenticationWindow.jsx new file mode 100755 index 00000000..49782559 --- /dev/null +++ b/frontend/src/components/AuthenticationWindow.jsx @@ -0,0 +1,476 @@ +import React, { useState, useEffect } from "react"; + +import theme from '../theme.jsx'; +import { v4 as uuidv4 } from "uuid"; +import { useAlert } from "react-alert"; + +import { + Divider, + MenuItem, + Button, + Dialog, + DialogTitle, + DialogActions, + DialogContent, + Textfield, + TextField, + Typography, + Select, + IconButton, +} from "@material-ui/core"; + +import { + LockOpen as LockOpenIcon, + Close as CloseIcon, +} from "@mui/icons-material"; + +import PaperComponent from "../components/PaperComponent.jsx" +import { useParams, useNavigate, Link } from "react-router-dom"; + +const AuthenticationData = (props) => { + const { + globalUrl, + selectedApp, + getAppAuthentication, + authenticationModalOpen, + setAuthenticationModalOpen, + + configureWorkflowModalOpen, + workflow, + setUpdate, + selectedAction, + setSelectedAction, + isLoggedIn, + authFieldsOnly, + } = props + + const alert = useAlert() + let navigate = useNavigate(); + const [submitSuccessful, setSubmitSuccessful] = useState(false) + const [authenticationOption, setAuthenticationOptions] = React.useState({ + app: JSON.parse(JSON.stringify(selectedApp)), + fields: {}, + label: "", + usage: [ + { + workflow_id: workflow === undefined ? "" : workflow.id, + }, + ], + id: uuidv4(), + active: true, + }); + + useEffect(() => { + if (isLoggedIn === false && authFieldsOnly !== true) { + navigate(`/login?view=${window.location.pathname}&message=Log in to authenticate this app`) + } + }, []) + + const setNewAppAuth = (appAuthData) => { + var headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + + // Find org_id and authorization from queries and add to headers + if (window.location.search !== "") { + const params = new URLSearchParams(window.location.search) + const org_id = params.get("org_id") + const authorization = params.get("authorization") + if (org_id !== null && authorization !== null) { + headers["Org-Id"] = org_id + headers["Authorization"] = "Bearer " + authorization + } + } + + fetch(globalUrl + "/api/v1/apps/authentication", { + method: "PUT", + headers: headers, + body: JSON.stringify(appAuthData), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for setting app auth :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + if (responseJson.reason === undefined) { + alert.error("Failed to set app auth. Are you logged in?") + } else { + alert.error("Failed to set app auth: " + responseJson.reason); + } + } else { + setSubmitSuccessful(true) + if (getAppAuthentication !== undefined) { + getAppAuthentication(true, false); + } + + if (setAuthenticationModalOpen !== undefined) { + setAuthenticationModalOpen(false) + } + } + }) + .catch((error) => { + //alert.error(error.toString()); + console.log("New auth error: ", error.toString()); + }); + }; + + if (selectedApp.authentication === undefined || selectedApp.authentication.parameters === null || + selectedApp.authentication.parameters === undefined || selectedApp.authentication.parameters.length === 0) { + + return ( + + + {selectedApp.name} does not require authentication + + + ); + } + + authenticationOption.app.actions = []; + + for (let paramkey in selectedApp.authentication.parameters) { + if ( + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] === undefined + ) { + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] = ""; + } + } + + const handleSubmitCheck = () => { + if (authenticationOption.label.length === 0) { + authenticationOption.label = `Auth for ${selectedApp.name}`; + } + + // Automatically mapping fields that already exist (predefined). + // Warning if fields are NOT filled + for (let paramkey in selectedApp.authentication.parameters) { + if ( + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ].length === 0 + ) { + if ( + selectedApp.authentication.parameters[paramkey].value !== undefined && + selectedApp.authentication.parameters[paramkey].value !== null && + selectedApp.authentication.parameters[paramkey].value.length > 0 + ) { + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] = selectedApp.authentication.parameters[paramkey].value; + } else { + if ( + selectedApp.authentication.parameters[paramkey].schema.type === "bool" + ) { + authenticationOption.fields[ + selectedApp.authentication.parameters[paramkey].name + ] = "false"; + } else { + alert.info( + "Field " + + selectedApp.authentication.parameters[paramkey].name + + " can't be empty" + ); + return; + } + } + } + } + + console.log("Action: ", selectedAction); + + if (selectedAction !== undefined) { + selectedAction.authentication_id = authenticationOption.id; + selectedAction.selectedAuthentication = authenticationOption; + if ( + selectedAction.authentication === undefined || + selectedAction.authentication === null + ) { + selectedAction.authentication = [authenticationOption]; + } else { + selectedAction.authentication.push(authenticationOption); + } + + setSelectedAction(selectedAction); + } + + var newAuthOption = JSON.parse(JSON.stringify(authenticationOption)); + var newFields = []; + console.log("Fields: ", newAuthOption.fields) + for (let authkey in newAuthOption.fields) { + const value = newAuthOption.fields[authkey]; + newFields.push({ + "key": authkey, + "value": value, + }); + } + + newAuthOption.fields = newFields; + setNewAppAuth(newAuthOption); + + if (configureWorkflowModalOpen === true) { + setSelectedAction({}); + } + + if (setUpdate !== undefined) { + setUpdate(authenticationOption.id); + } + }; + + if (authenticationOption.label === null || authenticationOption.label === undefined) { + authenticationOption.label = selectedApp.name + " authentication"; + } + + const authenticationParameters = selectedApp.authentication.parameters.map((data, index) => { + return ( +
+
+ + + {data.name.replace("_basic", "", -1).replace("_", " ", -1)} + +
+ + {data.schema !== undefined && + data.schema !== null && + data.schema.type === "bool" ? ( + + ) : ( + { + authenticationOption.fields[data.name] = + event.target.value; + }} + /> + )} +
+ ); + }) + + const authenticationButtons = + + {authFieldsOnly === true ? null : + + } + + + // Check if only the auth items should show + if (authFieldsOnly === true) { + return ( +
+ {submitSuccessful === true ? + + App succesfully configured! You may close this window. + + : + + {authenticationParameters} + {authenticationButtons} + + } +
+ ) + } + + return ( + { + //if (configureWorkflowModalOpen) { + // setSelectedAction({}); + //} + setAuthenticationModalOpen(false); + }} + PaperProps={{ + style: { + pointerEvents: "auto", + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: 600, + minHeight: 600, + maxHeight: 600, + padding: 15, + overflow: "hidden", + zIndex: 10012, + border: theme.palette.defaultBorder, + }, + }} + > + { + setAuthenticationModalOpen(false); + if (configureWorkflowModalOpen === true) { + setSelectedAction({}); + } + }} + > + + + +
+ Authentication for {selectedApp.name} +
+
+ + + What is app authentication? + +
+ These are required fields for authenticating with {selectedApp.name} +
+ Name - what is this used for? + { + authenticationOption.label = event.target.value; + }} + /> + +
+ {authenticationParameters} + + + + {authenticationButtons} + +
+ ); +}; + +export default AuthenticationData; diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 12ec0c1c..61084753 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -40,8 +40,7 @@ import { Business as BusinessIcon, Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon, -} from "@material-ui/icons"; -import data from "./frameworkStyle.jsx"; +} from "@mui/icons-material"; const scrollStyle1 = { height: 100, diff --git a/frontend/src/components/CreatorGrid.jsx b/frontend/src/components/CreatorGrid.jsx index 45e1961f..a6b6cbac 100644 --- a/frontend/src/components/CreatorGrid.jsx +++ b/frontend/src/components/CreatorGrid.jsx @@ -4,7 +4,12 @@ import ReactGA from 'react-ga4'; import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { + SkipNext as SkipNextIcon, + SkipPrevious as SkipPreviousIcon, + PlayArrow as PlayArrowIcon, + VerifiedUser as VerifiedUserIcon, + Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; import algoliasearch from 'algoliasearch/lite'; import { InstantSearch, Configure, connectSearchBox, connectHits } from 'react-instantsearch-dom'; @@ -31,14 +36,6 @@ import { AvatarGroup, } from "@mui/material" -import { - SkipNext as SkipNextIcon, - SkipPrevious as SkipPreviousIcon, - PlayArrow as PlayArrowIcon, - VerifiedUser as VerifiedUserIcon, -} from "@material-ui/icons"; - - const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const CreatorGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props diff --git a/frontend/src/components/DocsGrid.jsx b/frontend/src/components/DocsGrid.jsx index ea20cd0e..4331c15b 100644 --- a/frontend/src/components/DocsGrid.jsx +++ b/frontend/src/components/DocsGrid.jsx @@ -4,7 +4,7 @@ import ReactGA from 'react-ga4'; import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; import aa from 'search-insights' import algoliasearch from 'algoliasearch/lite'; @@ -26,7 +26,7 @@ import { ListItemText, } from '@material-ui/core'; -import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons' + const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const DocsGrid = props => { @@ -182,7 +182,7 @@ const DocsGrid = props => { //const secondaryText = data.data !== undefined ? data.data.slice(0, 100)+"..." : "" const secondaryText = data.data !== undefined ? data.data.slice(0, 100)+"..." : "" - const baseImage = + const baseImage = const avatar = data.image_url === undefined ? baseImage : diff --git a/frontend/src/components/Dropzone.jsx b/frontend/src/components/Dropzone.jsx index 6ea9f0de..911bd87b 100755 --- a/frontend/src/components/Dropzone.jsx +++ b/frontend/src/components/Dropzone.jsx @@ -1,6 +1,8 @@ import React, { useRef, useState } from "react"; import { useEffect } from "react"; -import BackupIcon from "@material-ui/icons/Backup"; +import { + Backup as BackupIcon +} from "@mui/icons-material"; const dragOverStyle = { backgroundColor: "rgba(0,0,0,0.8)", diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 22a9d9ea..3ceaaf23 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -44,7 +44,7 @@ import { ExpandMore as ExpandMoreIcon, Publish as PublishIcon, OpenInNew as OpenInNewIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const EditWorkflow = (props) => { const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index a3f4e520..c2a00240 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -27,7 +27,7 @@ import { Publish as PublishIcon, Clear as ClearIcon, Add as AddIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import { useAlert } from "react-alert"; import Dropzone from "../components/Dropzone.jsx"; diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 975df6b7..8e27de82 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -34,12 +34,9 @@ import { Description as DescriptionIcon, EmojiObjects as EmojiObjectsIcon, Business as BusinessIcon, -} from '@material-ui/icons'; - -import { Analytics as AnalyticsIcon, Lightbulb as LightbulbIcon, -} from "@mui/icons-material"; +} from '@mui/icons-material'; import { useAlert } from "react-alert"; diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 2f1b765e..2c7e3623 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -9,9 +9,12 @@ import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import { useAlert } from "react-alert"; import IconButton from "@material-ui/core/IconButton"; -import ExpandLessIcon from "@material-ui/icons/ExpandLess"; -import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; -import SaveIcon from "@material-ui/icons/Save"; + +import { + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, + Save as SaveIcon, +} from "@mui/icons-material"; const useStyles = makeStyles({ notchedOutline: { diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index f2691e54..862fa503 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -26,9 +26,11 @@ import { } from "@material-ui/core"; import IconButton from "@material-ui/core/IconButton"; -import ExpandLessIcon from "@material-ui/icons/ExpandLess"; -import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; -import SaveIcon from "@material-ui/icons/Save"; +import { + ExpandLess as ExpandLessIcon, + ExpandMore as ExpandMoreIcon, + Save as SaveIcon, +} from "@mui/icons-material"; const useStyles = makeStyles({ notchedOutline: { @@ -699,4 +701,4 @@ const OrgHeaderexpanded = (props) => { ) } -export default OrgHeaderexpanded; \ No newline at end of file +export default OrgHeaderexpanded; diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 1871dee7..964fe2d9 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -5,7 +5,8 @@ import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths } from "../views/Apps.jsx"; import { sortByKey } from "../views/AngularWorkflow.jsx"; import { useTheme } from "@material-ui/core/styles"; -import NestedMenuItem from "material-ui-nested-menu-item"; +//import NestedMenuItem from "material-ui-nested-menu-item-v5"; +import { NestedMenuItem } from "mui-nested-menu"; import { useAlert } from "react-alert"; import theme from '../theme.jsx'; diff --git a/frontend/src/components/Searchfield.js b/frontend/src/components/Searchfield.js index 43fa7934..0a86014d 100644 --- a/frontend/src/components/Searchfield.js +++ b/frontend/src/components/Searchfield.js @@ -2,7 +2,6 @@ import React, {useState, useEffect, useRef} from 'react'; import { useNavigate, Link, useParams } from "react-router-dom"; import { useTheme } from '@material-ui/core/styles'; -import SearchIcon from '@material-ui/icons/Search'; import { Chip, @@ -23,7 +22,7 @@ import { AvatarGroup, } from "@mui/material" -import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons' +import {Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon} from '@mui/icons-material' import algoliasearch from 'algoliasearch/lite'; import aa from 'search-insights' @@ -158,7 +157,7 @@ const SearchField = props => { } var type = "workflows" - const baseImage = + const baseImage = return ( diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 37e44bd5..4a13bd8d 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -2,7 +2,6 @@ import React, {useState, useEffect, useRef} from 'react'; import { useNavigate, Link, useParams } from "react-router-dom"; import { useTheme } from '@material-ui/core/styles'; -import SearchIcon from '@material-ui/icons/Search'; import { Chip, @@ -23,7 +22,7 @@ import { AvatarGroup, } from "@mui/material" -import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons' +import {Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon} from '@mui/icons-material' import algoliasearch from 'algoliasearch/lite'; import aa from 'search-insights' @@ -162,7 +161,7 @@ const SearchField = props => { } var type = "workflows" - const baseImage = + const baseImage = return ( diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 50b159a9..f3eccb15 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -18,7 +18,7 @@ import theme from '../theme.jsx'; import Checkbox from '@mui/material/Checkbox'; import { orange } from '@mui/material/colors'; import { isMobile } from "react-device-detect" -import NestedMenuItem from "material-ui-nested-menu-item"; +import { NestedMenuItem } from "mui-nested-menu" import { GetParsedPaths, FindJsonPath } from "../views/Apps.jsx"; import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx"; diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index c086790d..b2c9d0be 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'; import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; import algoliasearch from 'algoliasearch/lite'; import { InstantSearch, Configure, connectSearchBox, connectHits } from 'react-instantsearch-dom'; diff --git a/frontend/src/components/WorkflowPaper.jsx b/frontend/src/components/WorkflowPaper.jsx index 516756d2..09cf01fc 100644 --- a/frontend/src/components/WorkflowPaper.jsx +++ b/frontend/src/components/WorkflowPaper.jsx @@ -19,7 +19,7 @@ import { Edit as EditIcon, BubbleChart as BubbleChartIcon, MoreVert as MoreVertIcon, -} from '@material-ui/icons'; +} from '@mui/icons-material'; import { useNavigate, Link, useParams } from "react-router-dom"; diff --git a/frontend/src/components/WorkflowPaperNew.jsx b/frontend/src/components/WorkflowPaperNew.jsx index 3234bdee..0efde264 100644 --- a/frontend/src/components/WorkflowPaperNew.jsx +++ b/frontend/src/components/WorkflowPaperNew.jsx @@ -20,7 +20,7 @@ import { Edit as EditIcon, BubbleChart as BubbleChartIcon, MoreVert as MoreVertIcon, -} from '@material-ui/icons'; +} from '@mui/icons-material'; import { useNavigate, Link, useParams } from "react-router-dom"; diff --git a/frontend/src/components/Workflowsearch.jsx b/frontend/src/components/Workflowsearch.jsx index 152fdd83..895d3fd0 100644 --- a/frontend/src/components/Workflowsearch.jsx +++ b/frontend/src/components/Workflowsearch.jsx @@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react'; import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; //import algoliasearch from 'algoliasearch/lite'; import algoliasearch from 'algoliasearch'; diff --git a/frontend/src/index.js b/frontend/src/index.js index 781cb802..338dbe71 100755 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,12 +1,16 @@ import React from "react"; -import ReactDOM from "react-dom"; -import "./index.css"; +import { createRoot } from "react-dom/client"; import App from "./App"; -import * as serviceWorker from "./serviceWorker"; -ReactDOM.render(, document.getElementById("root")); +//import "./index.css"; +//import reportWebVitals from "./reportWebVitals"; -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: http://bit.ly/CRA-PWA -serviceWorker.unregister(); +const rootElement = document.getElementById("root"); +const root = createRoot(rootElement); +root.render( + + + +); + +//reportWebVitals(); diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 2082cf2a..0b8ff7c2 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -7,9 +7,7 @@ import CodeEditor from "../components/ShuffleCodeEditor.jsx"; import getLocalCodeData from "../components/ShuffleCodeEditor.jsx"; import CacheView from "../components/CacheView.jsx"; import theme from "../theme.jsx"; -import AddIcon from "@mui/icons-material/Add"; -import ClearIcon from '@mui/icons-material/Clear'; -import StorageIcon from '@mui/icons-material/Storage'; + //import ToggleButton from '@mui/material/ToggleButton'; import { FormControl, @@ -57,7 +55,7 @@ import { OpenInNew as OpenInNewIcon, CloudDownload as CloudDownloadIcon, Description as DescriptionIcon, - Polymer as PolymerIcon, + Code as CodeIcon, CheckCircle as CheckCircleIcon, Close as CloseIcon, Apps as AppsIcon, @@ -66,13 +64,15 @@ import { 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, -} from "@material-ui/icons"; + Visibility as VisibilityIcon, + VisibilityOff as VisibilityOffIcon, + Add as AddIcon, + Clear as ClearIcon, + Storage as StorageIcon, +} from "@mui/icons-material"; import { useAlert } from "react-alert"; import Dropzone from "../components/Dropzone.jsx"; @@ -182,6 +182,11 @@ const Admin = (props) => { const [billingInfo, setBillingInfo] = React.useState({}); const [selectedStatus, setSelectedStatus] = React.useState([]); + useEffect(() => { + getUsers() + }, []); + + useEffect(() => { if (isDropzone) { //redirectOpenApi(); @@ -987,6 +992,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user leads.push("student") } + if (responseJson.lead_info.internal) { + leads.push("internal") + } + setSelectedStatus(leads) } @@ -1485,10 +1494,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }); }; - useEffect(() => { - getUsers() - }, []); - const getSettings = () => { fetch(globalUrl + "/api/v1/getsettings", { method: "GET", @@ -2186,7 +2191,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user primary: "Workflows", secondary: "", active: true, - icon: , + icon: , }, { primary: "Apps", @@ -2386,7 +2391,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user renderValue={(selected) => selected.join(', ')} MenuProps={MenuProps} > - {["contacted", "lead", "pov", "demo done", "customer", "student", ].map((name) => ( + {["contacted", "lead", "pov", "demo done", "customer", "student", "internal"].map((name) => ( -1} /> @@ -2755,7 +2760,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user item.usage === null ? 0 : item.usage, data_collection: "None", active: item.active, - icon: , + icon: , }; return ( @@ -4356,7 +4361,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user - + Environments /> diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 36c82003..ad55179a 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -10,7 +10,7 @@ import { useNavigate, Link, useParams } from "react-router-dom"; // import { Prompt } from "react-router"; // FIXME import { useBeforeunload } from "react-beforeunload"; import ReactJson from "react-json-view"; -import NestedMenuItem from "material-ui-nested-menu-item"; +import { NestedMenuItem } from "mui-nested-menu" import ReactMarkdown from "react-markdown"; import { useAlert } from "react-alert"; import theme from '../theme.jsx'; @@ -87,7 +87,7 @@ import { ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, - Polymer as PolymerIcon, + Code as CodeIcon, FormatListNumbered as FormatListNumberedIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, @@ -108,15 +108,12 @@ import { AddComment as AddCommentIcon, Edit as EditIcon, Send as SendIcon, -} from "@material-ui/icons"; - -import { Preview as PreviewIcon, ContentCopy as ContentCopyIcon, Circle as CircleIcon, SquareFoot as SquareFootIcon, AutoFixHigh as AutoFixHighIcon, -} from '@mui/icons-material'; +} from "@mui/icons-material"; import Autocomplete from "@material-ui/lab/Autocomplete"; @@ -4998,6 +4995,8 @@ const AngularWorkflow = (defaultprops) => { const edgeData = event.target.data(); if (edgeData.decorator === true) { return; + + } const cytoscapeElement = document.getElementById("cytoscape_view") @@ -12162,7 +12161,7 @@ const AngularWorkflow = (defaultprops) => { margin: "0px 0px 0px 0px", }} > - + Workflows diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index b040bb99..f9173292 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -39,11 +39,8 @@ import { ZoomInOutlined as ZoomInOutlinedIcon, ZoomOutOutlined as ZoomOutOutlinedIcon, Loop as LoopIcon, -} from "@material-ui/icons"; - -import { AddPhotoAlternate as AddPhotoAlternateIcon, -} from '@mui/icons-material'; +} from "@mui/icons-material"; import { v4 as uuidv4 } from "uuid"; import { Link, useParams } from "react-router-dom"; diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 44c3e594..52a458be 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -46,7 +46,7 @@ import { Search as SearchIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import { ForkRight as ForkRightIcon, diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index 15549a36..58cc7fec 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -33,7 +33,7 @@ import { CheckBox as CheckBoxIcon, CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon, OpenInNew as OpenInNewIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import WorkflowPaper from "../components/WorkflowPaper.jsx" import { removeParam } from "../views/AngularWorkflow.jsx" diff --git a/frontend/src/views/DashboardViews.jsx b/frontend/src/views/DashboardViews.jsx index 19c678fa..63745485 100644 --- a/frontend/src/views/DashboardViews.jsx +++ b/frontend/src/views/DashboardViews.jsx @@ -33,7 +33,7 @@ import { CheckBox as CheckBoxIcon, CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon, OpenInNew as OpenInNewIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; import WorkflowPaper from "../components/WorkflowPaper.jsx" import { removeParam } from "../views/AngularWorkflow.jsx" diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index e0d974f5..2e229d21 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -23,7 +23,7 @@ import { import { Link as LinkIcon, Edit as EditIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const Body = { //maxWidth: 1000, diff --git a/frontend/src/views/Faq.jsx b/frontend/src/views/Faq.jsx index 42d24261..46296ea2 100644 --- a/frontend/src/views/Faq.jsx +++ b/frontend/src/views/Faq.jsx @@ -3,7 +3,7 @@ import {isMobile} from "react-device-detect"; import {Link} from 'react-router-dom'; import {Divider, List, ListItem, ListItemText, Card, CardContent, Grid, Typography, Button, ButtonGroup, FormControl, Dialog, DialogTitle, DialogActions, DialogContent, Tooltip} from '@material-ui/core'; -import {ExpandMore as ExpandMoreIcon, ExpandLess as ExpandLessIcon} from '@material-ui/icons'; +import {ExpandMore as ExpandMoreIcon, ExpandLess as ExpandLessIcon} from '@mui/icons-material'; const hrefStyle = { textDecoration: "none", @@ -255,4 +255,4 @@ const Faq = (props) => { ) } -export default Faq; \ No newline at end of file +export default Faq; diff --git a/frontend/src/views/GettingStarted.jsx b/frontend/src/views/GettingStarted.jsx index 87dd1be0..e6114356 100644 --- a/frontend/src/views/GettingStarted.jsx +++ b/frontend/src/views/GettingStarted.jsx @@ -58,7 +58,6 @@ import { CloudDownload as CloudDownloadIcon, } from "@mui/icons-material"; -import NestedMenuItem from "material-ui-nested-menu-item"; //import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; //https://next.material-ui.com/components/material-icons/ @@ -1061,13 +1060,6 @@ const GettingStarted = (props) => { {"Duplicate Workflow"} - {/*= 0} style={{backgroundColor: inputColor, color: "white"}} onClick={() => { - //copyWorkflow(data) - //setOpen(false) - }} key={"duplicate"}> - - {"Copy to Child Org"} - */} { diff --git a/frontend/src/views/Landingpage.jsx b/frontend/src/views/Landingpage.jsx index ed02060f..a0de1eee 100755 --- a/frontend/src/views/Landingpage.jsx +++ b/frontend/src/views/Landingpage.jsx @@ -4,9 +4,11 @@ import Paper from "@material-ui/core/Paper"; import Button from "@material-ui/core/Button"; import Divider from "@material-ui/core/Divider"; import { BrowserView, MobileView } from "react-device-detect"; -import ScheduleIcon from "@material-ui/icons/Schedule"; -import Web from "@material-ui/icons/Web"; -import AccountTree from "@material-ui/icons/AccountTree"; +import { + Schedule as ScheduleIcon, + Web as WebIcon, + AccountTree as AccountTreeIcon, +} from "@mui/icons-material"; const bodyDivStyle = { margin: "auto", diff --git a/frontend/src/views/LandingpageNew.jsx b/frontend/src/views/LandingpageNew.jsx index 252d389c..c859beee 100755 --- a/frontend/src/views/LandingpageNew.jsx +++ b/frontend/src/views/LandingpageNew.jsx @@ -11,12 +11,14 @@ import Divider from "@material-ui/core/Divider"; import Grid from "@material-ui/core/Grid"; import { BrowserView, MobileView } from "react-device-detect"; -import ScheduleIcon from "@material-ui/icons/Schedule"; -import Web from "@material-ui/icons/Web"; -import AccountTree from "@material-ui/icons/AccountTree"; -import InfoIcon from "@material-ui/icons/Info"; -import ArrowForwardIcon from "@material-ui/icons/ArrowForward"; -import CreateIcon from "@material-ui/icons/Create"; +import { + Schedule as ScheduleIcon, + Web as WebIcon, + AccountTree as AccountTreeIcon, + Info as InfoIcon, + ArrowForward as ArrowForwardIcon, + Create as CreateIcon, +} from "@mui/icons-material"; const bodyDivStyle = { margin: "auto", @@ -86,7 +88,7 @@ const LandingPage = (props) => { "Simple integrations", "Easily use others' or create your own integration", "/docs/features", - ), @@ -94,7 +96,7 @@ const LandingPage = (props) => { "Workflows", "Access the power of automation within minutes, whether its on premise or in the cloud", "/docs/features", - ), diff --git a/frontend/src/views/Search.jsx b/frontend/src/views/Search.jsx index 942a1ec9..79ccca1a 100644 --- a/frontend/src/views/Search.jsx +++ b/frontend/src/views/Search.jsx @@ -15,10 +15,10 @@ import { import { Apps as AppsIcon, - Polymer as PolymerIcon, + Code as CodeIcon, EmojiObjects as EmojiObjectsIcon, Description as DescriptionIcon, -} from "@material-ui/icons"; +} from "@mui/icons-material"; const bodyDivStyle = { @@ -137,7 +137,7 @@ const Search = (props) => { /> - Workflows + Workflows /> { ) : null} - {userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") ? + {userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ?
From 65574a1601e6226c891a295852257f926950e501 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 12 Jul 2023 02:09:12 +0200 Subject: [PATCH 004/160] Fixed mui core finally --- frontend/package.json | 12 +- frontend/src/App.jsx | 51 +- frontend/src/components/AlertTemplate.js | 5 +- frontend/src/components/AppFramework.jsx | 5 +- frontend/src/components/AppGrid.jsx | 5 +- frontend/src/components/Appsearch.jsx | 5 +- frontend/src/components/AppsearchPopout.jsx | 2 +- .../src/components/AuthenticationItem.jsx | 2 +- .../src/components/AuthenticationNormal.jsx | 2 +- .../src/components/AuthenticationWindow.jsx | 2 +- frontend/src/components/Billing.jsx | 5 +- frontend/src/components/Branding.jsx | 3 +- frontend/src/components/CacheView.jsx | 2 +- frontend/src/components/ConfigureWorkflow.jsx | 2 +- frontend/src/components/CreatorGrid.jsx | 3 +- frontend/src/components/DocsGrid.jsx | 5 +- frontend/src/components/EditWorkflow.jsx | 2 +- frontend/src/components/Files.jsx | 2 +- frontend/src/components/Header.jsx | 3 +- .../src/components/LandingpageUsecases.jsx | 2 +- frontend/src/components/Newsletter.jsx | 3 +- frontend/src/components/Oauth2Auth.jsx | 3 +- frontend/src/components/OrgHeader.js | 3 +- frontend/src/components/OrgHeader.jsx | 4 +- frontend/src/components/OrgHeaderexpanded.jsx | 9 +- frontend/src/components/PaperComponent.jsx | 2 +- frontend/src/components/ParsedAction.jsx | 9 +- frontend/src/components/Priorities.jsx | 2 +- frontend/src/components/Priority.jsx | 2 +- frontend/src/components/Searchfield.js | 647 ------------------ frontend/src/components/Searchfield.jsx | 5 +- frontend/src/components/SecurityFramework.jsx | 2 +- frontend/src/components/ShuffleCodeEditor.jsx | 2 +- .../src/components/SuggestedWorkflows.jsx | 2 +- frontend/src/components/UsecaseSearch.jsx | 2 +- frontend/src/components/WelcomeForm2.jsx | 4 +- frontend/src/components/WorkflowGrid.jsx | 5 +- frontend/src/components/WorkflowPaper.jsx | 2 +- frontend/src/components/WorkflowPaperNew.jsx | 2 +- frontend/src/components/Workflowsearch.jsx | 5 +- frontend/src/theme.js | 4 +- frontend/src/theme.jsx | 4 +- frontend/src/views/Admin.jsx | 4 +- frontend/src/views/AdminSetup.jsx | 4 +- frontend/src/views/AngularWorkflow.jsx | 10 +- frontend/src/views/AppCreator.jsx | 7 +- frontend/src/views/Apps.jsx | 3 +- frontend/src/views/Dashboard.jsx | 6 +- frontend/src/views/DashboardViews.jsx | 6 +- frontend/src/views/Docs.jsx | 3 +- frontend/src/views/Faq.jsx | 2 +- frontend/src/views/FrameworkWrapper.jsx | 2 +- frontend/src/views/GettingStarted.jsx | 7 +- frontend/src/views/HandlePaymentNew.jsx | 2 +- frontend/src/views/Introduction.jsx | 1 - frontend/src/views/LoginPage.jsx | 7 +- frontend/src/views/RunWorkflow.jsx | 4 +- frontend/src/views/Search.jsx | 2 +- frontend/src/views/Services.jsx | 2 +- frontend/src/views/SetAuthentication.jsx | 2 +- frontend/src/views/SetAuthenticationSSO.jsx | 2 +- frontend/src/views/SettingsPage.jsx | 3 +- frontend/src/views/TempDashboard.jsx | 2 +- frontend/src/views/UpdateAuthentication.jsx | 2 +- frontend/src/views/Welcome.jsx | 8 +- frontend/src/views/Workflows.jsx | 4 +- 66 files changed, 111 insertions(+), 827 deletions(-) delete mode 100644 frontend/src/components/Searchfield.js diff --git a/frontend/package.json b/frontend/package.json index 2efb1929..157ceefc 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,13 +9,10 @@ "@emotion/react": "^11.7.0", "@emotion/styled": "^11.6.0", "@lezer/highlight": "^1.1.3", - "@material-ui/core": "^4.5.2", - "@material-ui/lab": "^4.0.0-alpha.58", - "@material-ui/styles": "^4.5.2", - "@material-ui/utils": "^4.11.2", "@metamask/detect-provider": "^1.2.0", "@mui/icons-material": "^5.14.0", "@mui/material": "^5.2.3", + "@mui/styles": "^5.14.0", "@mui/x-data-grid": "^5.17.11", "@uiw/codemirror-themes": "^4.19.9", "@uiw/react-codemirror": "^3.2.1", @@ -37,6 +34,13 @@ "github-markdown-css": "^3.0.1", "import": "0.0.6", "interweave": "^11.2.0", + "jss": "^10.10.0", + "jss-camel-case": "^6.1.0", + "jss-default-unit": "^8.0.2", + "jss-global": "^3.0.0", + "jss-nested": "^6.0.1", + "jss-props-sort": "^6.0.0", + "jss-vendor-prefixer": "^8.0.1", "material-icons": "^0.7.7", "material-icons-react": "^1.0.4", "material-ui-chip-input": "^2.0.0-beta.2", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a70f701e..dbb513fe 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -6,7 +6,6 @@ import { removeCookies, useCookies } from "react-cookie"; import Workflows from "./views/Workflows"; import GettingStarted from "./views/GettingStarted"; -import EditWebhook from "./views/EditWebhook"; import AngularWorkflow from "./views/AngularWorkflow.jsx"; import Header from "./components/Header.jsx"; @@ -20,13 +19,12 @@ import DashboardView from "./views/DashboardViews.jsx"; import AdminSetup from "./views/AdminSetup"; import Admin from "./views/Admin"; import Docs from "./views/Docs.jsx"; -import Introduction from "./views/Introduction"; +//import Introduction from "./views/Introduction"; import SetAuthentication from "./views/SetAuthentication"; import SetAuthenticationSSO from "./views/SetAuthenticationSSO"; import Search from "./views/Search.jsx"; import RunWorkflow from "./views/RunWorkflow.jsx"; -import LandingPageNew from "./views/LandingpageNew"; import LoginPage from "./views/LoginPage"; import SettingsPage from "./views/SettingsPage"; import KeepAlive from "./views/KeepAlive.jsx"; @@ -42,7 +40,6 @@ import { isMobile } from "react-device-detect"; import detectEthereumProvider from "@metamask/detect-provider"; import Drift from "react-driftjs"; -import DashboardPage from "./views/TempDashboard.jsx"; // Production - backend proxy forwarding in nginx var globalUrl = window.location.origin; @@ -282,18 +279,6 @@ const App = (message, props) => { } const includedData = - window.location.pathname === "/home" || - window.location.pathname === "/features" ? ( -
- - } - /> - -
- ) : (
{ /> } /> - - } - /> - - } - /> { {...props} /> } - /> - - } /> { />
- ); //
// backgroundColor: "#213243", diff --git a/frontend/src/components/AlertTemplate.js b/frontend/src/components/AlertTemplate.js index e7bf849d..3b320a6b 100755 --- a/frontend/src/components/AlertTemplate.js +++ b/frontend/src/components/AlertTemplate.js @@ -5,7 +5,10 @@ import { ErrorOutline as ErrorOutlineIcon, Close as CloseIcon, } from "@mui/icons-material"; -import Typography from "@material-ui/core/Typography"; + +import { + Typography +} from "@mui/material"; const alertStyle = { backgroundColor: "rgba(0,0,0,0.9)", diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index 9d965727..54fb6869 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -23,11 +23,8 @@ import { Dialog, Chip, Avatar, -} from "@material-ui/core"; - -import { Button -} from '@material-ui/core'; +} from "@mui/material"; import { Close as CloseIcon, diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index fc01dccd..1e6ab296 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -1,7 +1,7 @@ import React, {useEffect, useState} from 'react'; +import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; import { @@ -25,7 +25,7 @@ import { Typography, Button, Tooltip -} from '@material-ui/core'; +} from '@mui/material'; const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") @@ -38,7 +38,6 @@ const AppGrid = props => { const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index 9f0699d2..5defd0f0 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; +import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; import { useAlert } from "react-alert"; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; @@ -8,7 +8,7 @@ import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } //import algoliasearch from 'algoliasearch/lite'; import algoliasearch from 'algoliasearch'; import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom'; -import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@material-ui/core'; +import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@mui/material'; import aa from 'search-insights' const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const Appsearch = props => { @@ -18,7 +18,6 @@ const Appsearch = props => { const alert = useAlert(); const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); diff --git a/frontend/src/components/AppsearchPopout.jsx b/frontend/src/components/AppsearchPopout.jsx index 2ba15ccf..1e76206b 100644 --- a/frontend/src/components/AppsearchPopout.jsx +++ b/frontend/src/components/AppsearchPopout.jsx @@ -12,7 +12,7 @@ import { CircularProgress, Tooltip, Button, -} from "@material-ui/core"; +} from "@mui/material"; import { Close as CloseIcon, diff --git a/frontend/src/components/AuthenticationItem.jsx b/frontend/src/components/AuthenticationItem.jsx index c8add063..06329a35 100644 --- a/frontend/src/components/AuthenticationItem.jsx +++ b/frontend/src/components/AuthenticationItem.jsx @@ -19,7 +19,7 @@ import { Typography, TextField, Zoom, -} from "@material-ui/core"; +} from "@mui/material"; import { Edit as EditIcon, diff --git a/frontend/src/components/AuthenticationNormal.jsx b/frontend/src/components/AuthenticationNormal.jsx index d6cd8e35..a0d6acab 100644 --- a/frontend/src/components/AuthenticationNormal.jsx +++ b/frontend/src/components/AuthenticationNormal.jsx @@ -13,7 +13,7 @@ import { DialogTitle, DialogContent, Typography, -} from "@material-ui/core"; +} from "@mui/material"; import { LockOpen as LockOpenIcon, diff --git a/frontend/src/components/AuthenticationWindow.jsx b/frontend/src/components/AuthenticationWindow.jsx index 49782559..47dd9b4e 100755 --- a/frontend/src/components/AuthenticationWindow.jsx +++ b/frontend/src/components/AuthenticationWindow.jsx @@ -17,7 +17,7 @@ import { Typography, Select, IconButton, -} from "@material-ui/core"; +} from "@mui/material"; import { LockOpen as LockOpenIcon, diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index b59b5e4a..71d01789 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; +import theme from "../theme.jsx"; import ReactGA from 'react-ga4'; -import { useTheme } from "@material-ui/core/styles"; import { Paper, Typography, @@ -9,7 +9,7 @@ import { Button, Grid, Card, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; @@ -17,7 +17,6 @@ import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; const Billing = (props) => { const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; console.log("Billing: ", billingInfo); - const theme = useTheme(); const alert = useAlert(); const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : "" diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index 40254bbb..4b44f5c1 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -2,7 +2,6 @@ import React, { useState, useEffect } from "react"; import ReactGA from 'react-ga4'; import theme from "../theme.jsx"; -import { useTheme } from "@material-ui/core/styles"; import { Paper, Typography, @@ -10,7 +9,7 @@ import { Button, Grid, Card, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 61084753..62394423 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -15,7 +15,7 @@ import { Dialog, DialogTitle, DialogActions, -} from "@material-ui/core"; +} from "@mui/material"; import { useAlert } from "react-alert"; import { diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index b7b9f2b0..f54b73e5 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -15,7 +15,7 @@ import { ListItem, ListItemText, Fade, -} from "@material-ui/core"; +} from "@mui/material"; import { FavoriteBorder as FavoriteBorderIcon, Error as ErrorIcon, diff --git a/frontend/src/components/CreatorGrid.jsx b/frontend/src/components/CreatorGrid.jsx index a6b6cbac..cfc3b3e2 100644 --- a/frontend/src/components/CreatorGrid.jsx +++ b/frontend/src/components/CreatorGrid.jsx @@ -1,7 +1,6 @@ import React, { useEffect, useState } from 'react'; import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; import { @@ -29,7 +28,7 @@ import { Zoom, CardMedia, CardActionArea, -} from '@material-ui/core'; +} from '@mui/material'; import { Avatar, diff --git a/frontend/src/components/DocsGrid.jsx b/frontend/src/components/DocsGrid.jsx index 4331c15b..fa194a25 100644 --- a/frontend/src/components/DocsGrid.jsx +++ b/frontend/src/components/DocsGrid.jsx @@ -1,7 +1,7 @@ import React, {useEffect, useState} from 'react'; +import theme from '../theme.jsx'; import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; @@ -24,7 +24,7 @@ import { ListItem, ListItemAvatar, ListItemText, -} from '@material-ui/core'; +} from '@mui/material'; @@ -33,7 +33,6 @@ const DocsGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 3ceaaf23..5e8b0493 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -37,7 +37,7 @@ import { FormControl, FormLabel, -} from "@material-ui/core"; +} from "@mui/material"; import { ExpandLess as ExpandLessIcon, diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index c2a00240..cd7a004b 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -15,7 +15,7 @@ import { Divider, Select, MenuItem, -} from "@material-ui/core"; +} from "@mui/material"; import { OpenInNew as OpenInNewIcon, diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 8e27de82..2a27e91a 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -1,6 +1,5 @@ import React, {useState} from 'react'; import {BrowserView, MobileView} from "react-device-detect"; -import { useTheme } from '@material-ui/core/styles'; import {Link} from 'react-router-dom'; import ReactGA from 'react-ga4'; @@ -21,7 +20,7 @@ import { IconButton, Divider, LinearProgress, -} from '@material-ui/core' +} from '@mui/material'; import { MeetingRoom as MeetingRoomIcon, diff --git a/frontend/src/components/LandingpageUsecases.jsx b/frontend/src/components/LandingpageUsecases.jsx index 9fd5d016..063e4a6b 100644 --- a/frontend/src/components/LandingpageUsecases.jsx +++ b/frontend/src/components/LandingpageUsecases.jsx @@ -4,7 +4,7 @@ import AppFramework, { usecases } from "../components/AppFramework.jsx"; import {Link} from 'react-router-dom'; import ReactGA from 'react-ga4'; -import { Button, LinearProgress, Typography } from '@material-ui/core'; +import { Button, LinearProgress, Typography } from '@mui/material'; export const securityFramework = [ { diff --git a/frontend/src/components/Newsletter.jsx b/frontend/src/components/Newsletter.jsx index ccadf77b..36a7519c 100644 --- a/frontend/src/components/Newsletter.jsx +++ b/frontend/src/components/Newsletter.jsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import { useTheme } from '@material-ui/core/styles'; +import theme from '../theme.jsx'; import {isMobile} from "react-device-detect"; import ReactGA from 'react-ga4'; @@ -8,7 +8,6 @@ import {TextField, Typography, Button} from '@material-ui/core'; const Newsletter = (props) => { const { globalUrl, } = props; - const theme = useTheme(); const [email, setEmail] = useState(""); const [msg, setMsg] = useState(""); const [buttonActive, setButtonActive] = useState(true); diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 9832b990..69b3a699 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -1,6 +1,5 @@ import React, { useRef, useState, useEffect, useLayoutEffect } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; -import { useTheme } from "@material-ui/core/styles"; import theme from '../theme.jsx'; import { useAlert } from "react-alert"; @@ -38,7 +37,7 @@ import { CircularProgress, Switch, Fade, -} from "@material-ui/core"; +} from "@mui/material"; import { LockOpen as LockOpenIcon, SupervisorAccount as SupervisorAccountIcon, diff --git a/frontend/src/components/OrgHeader.js b/frontend/src/components/OrgHeader.js index 65dd6e97..6f87bf23 100755 --- a/frontend/src/components/OrgHeader.js +++ b/frontend/src/components/OrgHeader.js @@ -1,6 +1,5 @@ import React, { useEffect} from 'react'; -import { makeStyles } from '@material-ui/styles'; -import { useTheme } from '@material-ui/core/styles'; +import { makeStyles } from '@mui/styles'; import Tooltip from '@material-ui/core/Tooltip'; import Grid from '@material-ui/core/Grid'; diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 2c7e3623..354ec93b 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -1,5 +1,6 @@ import React, { useEffect } from "react"; -import { makeStyles } from "@material-ui/styles"; +import theme from '../theme.jsx'; +import { makeStyles } from "@mui/styles"; import { useTheme } from "@material-ui/core/styles"; import Tooltip from "@material-ui/core/Tooltip"; @@ -36,7 +37,6 @@ const OrgHeader = (props) => { handleEditOrg, } = props; - const theme = useTheme(); const alert = useAlert(); const classes = useStyles(); diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 862fa503..6a508aa6 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -1,8 +1,8 @@ import React, { useEffect } from "react"; -import { makeStyles } from "@material-ui/styles"; -import { useTheme } from "@material-ui/core/styles"; +import { makeStyles } from "@mui/styles"; import { useAlert } from "react-alert"; +import theme from '../theme.jsx'; import { FormControl, @@ -23,9 +23,9 @@ import { Tabs, Tab, Grid, -} from "@material-ui/core"; + IconButton, +} from "@mui/material"; -import IconButton from "@material-ui/core/IconButton"; import { ExpandLess as ExpandLessIcon, ExpandMore as ExpandMoreIcon, @@ -48,7 +48,6 @@ const OrgHeaderexpanded = (props) => { adminTab, } = props; - const theme = useTheme(); const alert = useAlert(); const classes = useStyles(); const defaultBranch = "master"; diff --git a/frontend/src/components/PaperComponent.jsx b/frontend/src/components/PaperComponent.jsx index 76fb56ef..d1e2dc22 100644 --- a/frontend/src/components/PaperComponent.jsx +++ b/frontend/src/components/PaperComponent.jsx @@ -3,7 +3,7 @@ import React, {useState, useEffect, useLayoutEffect} from 'react'; import Draggable from "react-draggable"; import { Paper -} from "@material-ui/core"; +} from "@mui/material"; const PaperComponent = (props) => { return ( diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 964fe2d9..6d0d6185 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1,10 +1,10 @@ import React, { useState, useEffect, useLayoutEffect } from "react"; -import { makeStyles, createStyles } from "@material-ui/core/styles"; +import { makeStyles, createStyles } from "@mui/styles"; + import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths } from "../views/Apps.jsx"; import { sortByKey } from "../views/AngularWorkflow.jsx"; -import { useTheme } from "@material-ui/core/styles"; //import NestedMenuItem from "material-ui-nested-menu-item-v5"; import { NestedMenuItem } from "mui-nested-menu"; import { useAlert } from "react-alert"; @@ -44,11 +44,8 @@ import { CircularProgress, Switch, Fade, -} from "@material-ui/core"; - -import { Autocomplete -} from "@material-ui/lab"; +} from "@mui/material"; import { HelpOutline as HelpOutlineIcon, diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 88bea907..6e0a872e 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -9,7 +9,7 @@ import { Grid, Card, Switch, -} from "@material-ui/core"; +} from "@mui/material"; import Priority from "../components/Priority.jsx"; import { useAlert } from "react-alert"; diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 0123b37d..8d571d79 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -9,7 +9,7 @@ import { Button, Grid, Card, -} from "@material-ui/core"; +} from "@mui/material"; // import magic wand icon from material ui icons import { diff --git a/frontend/src/components/Searchfield.js b/frontend/src/components/Searchfield.js deleted file mode 100644 index 0a86014d..00000000 --- a/frontend/src/components/Searchfield.js +++ /dev/null @@ -1,647 +0,0 @@ -import React, {useState, useEffect, useRef} from 'react'; - -import { useNavigate, Link, useParams } from "react-router-dom"; -import { useTheme } from '@material-ui/core/styles'; - -import { - Chip, - IconButton, - TextField, - InputAdornment, - List, - Card, - ListItem, - ListItemAvatar, - ListItemText, - Avatar, - Typography, - Tooltip, -} from '@material-ui/core'; - -import { - AvatarGroup, -} from "@mui/material" - -import {Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon} from '@mui/icons-material' - -import algoliasearch from 'algoliasearch/lite'; -import aa from 'search-insights' -import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; -//import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; - -// https://www.algolia.com/doc/api-reference/widgets/search-box/react/ -const chipStyle = { - backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white", -} - -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") -const SearchField = props => { - const { serverside, userdata } = props - - const theme = useTheme(); - let navigate = useNavigate(); - const borderRadius = 3 - const node = useRef() - const [searchOpen, setSearchOpen] = useState(false) - const [oldPath, setOldPath] = useState("") - - if (serverside === true) { - return null - } - - if (window !== undefined && window.location !== undefined && window.location.pathname === "/search") { - return null - } - - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - - if (window.location.pathname !== oldPath) { - setSearchOpen(false) - setOldPath(window.location.pathname) - } - - //useEffect(() => { - // if (searchOpen) { - // var tarfield = document.getElementById("shuffle_search_field") - // tarfield.focus() - // } - //}, searchOpen) - - const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => { - - /* - endAdornment: ( - { - event.preventDefault() - }}> - { - setSearchOpen(false) - }} /> - - ), - */ - - return ( -
- //
- // backgroundColor: "#213243", - // This is a mess hahahah return ( - + @@ -678,7 +675,7 @@ const App = (message, props) => { - + ); }; diff --git a/frontend/src/components/AppGrid1.jsx b/frontend/src/components/AppGrid1.jsx deleted file mode 100644 index b64610c5..00000000 --- a/frontend/src/components/AppGrid1.jsx +++ /dev/null @@ -1,377 +0,0 @@ -import React, {useEffect, useState} from 'react'; - -import ReactGA from 'react-ga4'; -import { useTheme } from '@material-ui/core/styles'; -import {Link} from 'react-router-dom'; - -import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons'; - -import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch, Configure, connectSearchBox, connectHits, connectHitInsights } from 'react-instantsearch-dom'; - -import aa from 'search-insights' - -import { - Zoom, - Grid, - Paper, - TextField, - ButtonBase, - InputAdornment, - Typography, - Button, - Tooltip -} from '@material-ui/core'; - -const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") -//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") -const AppGrid1 = props => { - const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, searchValue } = props - - const isCloud = - window.location.host === "localhost:3000" || - window.location.host === "shuffler.io"; - - const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows - const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs - const theme = useTheme(); - //const [apps, setApps] = React.useState([]); - //const [filteredApps, setFilteredApps] = React.useState([]); - const [formMail, setFormMail] = React.useState(""); - const [message, setMessage] = React.useState(""); - const [formMessage, setFormMessage] = React.useState(""); - - const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} - - const innerColor = "rgba(255,255,255,0.65)" - const borderRadius = 3 - window.title = "Shuffle | Apps | Find and integrate any app" - - const submitContact = (email, message) => { - const data = { - "firstname": "", - "lastname": "", - "title": "", - "companyname": "", - "email": email, - "phone": "", - "message": message, - } - - const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly." - - fetch(globalUrl+"/api/v1/contact", { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }) - .then(response => response.json()) - .then(response => { - if (response.success === true) { - setFormMessage(response.reason) - //alert.info("Thanks for submitting!") - } else { - setFormMessage(errorMessage) - } - - setFormMail("") - setMessage("") - }) - .catch(error => { - setFormMessage(errorMessage) - console.log(error) - }); - } - - const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { - - useEffect(() => { - if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { - const urlSearchParams = new URLSearchParams(window.location.search) - const params = Object.fromEntries(urlSearchParams.entries()) - const foundQuery = {searchValue} - if (foundQuery !== null && foundQuery !== undefined) { - console.log("Got query: ", foundQuery) - refine(foundQuery) - } - } - }, []) - - return ( -
- - - - ), - }} - autoComplete='off' - type="hidden" - color="primary" - defaultValue={currentRefinement} - placeholder="Find Apps..." - id="shuffle_search_field" - onChange={(event) => { - refine(event.currentTarget.value) - }} - limit={5} - /> - {/*isSearchStalled ? 'My search is stalled' : ''*/} - - ) - } - - var workflowDelay = -50 - const Hits = ({ hits, insights }) => { - const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) - var counted = 0 - - //console.log(hits) - //var curhits = hits - //if (hits.length > 0 && defaultApps.length === 0) { - // setDefaultApps(hits) - //} - - //const [defaultApps, setDefaultApps] = React.useState([]) - //console.log(hits) - //if (hits.length > 0 && hits.length !== innerHits.length) { - // setInnerHits(hits) - //} - - return ( - - {hits.map((data, index) => { - - workflowDelay += 50 - - const paperStyle = { - backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, - color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", - border: `1px solid ${innerColor}`, - padding: 15, - cursor: "pointer", - position: "relative", - minHeight: 116, - } - - if (counted === 12/xs*rowHandler) { - return null - } - - counted += 1 - var parsedname = "" - for (var key = 0; key < data.name.length; key++) { - var character = data.name.charAt(key) - if (character === character.toUpperCase()) { - //console.log(data.name[key], data.name[key+1]) - if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) { - } else { - parsedname += " " - } - } - - parsedname += character - } - - parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ") - - return ( - - - - { - setMouseHoverIndex(index) - /* - ReactGA.event({ - category: "app_grid_view", - action: `search_bar_click`, - label: "", - }) - */ - }} onMouseOut={() => { - setMouseHoverIndex(-1) - }} onClick={() => { - if (isCloud) { - ReactGA.event({ - category: "app_grid_view", - action: `app_${parsedname}_${data.id}_click`, - label: "", - }) - } - - //const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6") - console.log(searchClient) - aa('init', { - appId: searchClient.appId, - apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"] - }) - - const timestamp = new Date().getTime() - aa('sendEvents', [ - { - eventType: 'click', - eventName: 'Product Clicked', - index: 'appsearch', - objectIDs: [data.objectID], - timestamp: timestamp, - queryID: data.__queryID, - positions: [data.__position], - userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id, - } - ]) - - }}> - - {data.name} - -
- {index === mouseHoverIndex || showName === true ? - parsedname - : - null - } - {data.generated ? - - {data.invalid ? - - : - - } - - : - - - - } - - - - - ) - })} - - ) - } - - const CustomSearchBox = connectSearchBox(SearchBox) - const CustomHits = connectHits(Hits) - //const CustomHits = connectHitInsights(aa)(Hits) - const selectButtonStyle = { - minWidth: 150, - maxWidth: 150, - minHeight: 50, - } - - return ( -
- {/* -
- -
- */} -
- -
- -
- - -
- {showSuggestion === true ? -
- - Can't find what you're looking for? - -
- setFormMail(e.target.value)} - /> - setMessage(e.target.value)} - /> -
- - {formMessage} -
- : null - } - - - - Search by - - - Algolia logo - - -
-
- ) -} - -export default AppGrid1; diff --git a/frontend/src/components/CreatorGrid.jsx b/frontend/src/components/CreatorGrid.jsx index cfc3b3e2..9d23b439 100644 --- a/frontend/src/components/CreatorGrid.jsx +++ b/frontend/src/components/CreatorGrid.jsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import ReactGA from 'react-ga4'; import {Link} from 'react-router-dom'; +import theme from '../theme.jsx'; import { SkipNext as SkipNextIcon, @@ -40,7 +41,6 @@ const CreatorGrid = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs - const theme = useTheme(); //const [apps, setApps] = React.useState([]); //const [filteredApps, setFilteredApps] = React.useState([]); const [formMail, setFormMail] = React.useState(""); diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js deleted file mode 100755 index 0934f2e6..00000000 --- a/frontend/src/components/Header.js +++ /dev/null @@ -1,883 +0,0 @@ -import React, { useState } from "react"; -import { BrowserView, MobileView } from "react-device-detect"; - -import { Link } from "react-router-dom"; - -import { useTheme } from "@material-ui/core/styles"; - -import { - Chip, - Badge, - Typography, - Paper, - Tooltip, - List, - Avatar, - Menu, - ListItem, - MenuItem, - Select, - Button, - IconButton, - Grid, -} from "@material-ui/core"; - -import { - MeetingRoom as MeetingRoomIcon, - Settings as SettingsIcon, - Notifications as NotificationsIcon, - Home as HomeIcon, - Polymer as PolymerIcon, - Apps as AppsIcon, - Description as DescriptionIcon, - HelpOutline as HelpOutlineIcon, -} from "@material-ui/icons"; - -import { - Analytics as AnalyticsIcon, - Lightbulb as LightbulbIcon, -} from "@mui/icons-material"; -//import LogoutIcon from '@mui/icons-material/Logout'; -import { useAlert } from "react-alert"; -import SearchField from '../components/Searchfield' - -const hoverColor = "#f85a3e"; -const hoverOutColor = "#e8eaf6"; - -const Header = (props) => { - const { - globalUrl, - setNotifications, - notifications, - isLoggedIn, - removeCookie, - homePage, - isLoaded, - userdata, - cookies, - } = props; - const theme = useTheme(); - - const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); - const [SoarHoverColor, setSoarHoverColor] = useState(hoverOutColor); - const [LoginHoverColor, setLoginHoverColor] = useState(hoverOutColor); - const [DocsHoverColor, setDocsHoverColor] = useState(hoverOutColor); - const [HelpHoverColor, setHelpHoverColor] = useState(hoverOutColor); - const [anchorEl, setAnchorEl] = React.useState(null); - const [anchorElAvatar, setAnchorElAvatar] = React.useState(null); - const alert = useAlert(); - - const hrefStyle = { - color: hoverOutColor, - textDecoration: "none", - }; - - const handleClose = () => { - setAnchorEl(null); - setAnchorElAvatar(null); - }; - - const clearNotifications = () => { - // Don't really care about the logout - fetch(`${globalUrl}/api/v1/notifications/clear`, { - credentials: "include", - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) - .then(function (response) { - if (response.status !== 200) { - console.log("Error in response"); - } - - return response.json(); - }) - .then(function (responseJson) { - if (responseJson.success === true) { - setNotifications([]); - handleClose(); - } else { - alert.error( - "Failed dismissing notifications. Please try again later." - ); - } - }) - .catch((error) => { - console.log("error in notification dismissal: ", error); - //removeCookie("session_token", {path: "/"}) - }); - }; - - const dismissNotification = (alert_id) => { - // Don't really care about the logout - fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { - credentials: "include", - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }) - .then(function (response) { - if (response.status !== 200) { - console.log("Error in response"); - } - - return response.json(); - }) - .then(function (responseJson) { - if (responseJson.success === true) { - const newNotifications = notifications.filter( - (data) => data.id !== alert_id - ); - console.log("NEW NOTIFICATIONS: ", newNotifications); - setNotifications(newNotifications); - } else { - alert.error( - "Failed dismissing notification. Please try again later." - ); - } - }) - .catch((error) => { - console.log("error in notification dismissal: ", error); - //removeCookie("session_token", {path: "/"}) - }); - }; - - // DEBUG HERE - const handleClickLogout = () => { - console.log("COOKIES: ", cookies, "Remover: ", removeCookie); - // Don't really care about the logout - fetch(globalUrl + "/api/v1/logout", { - credentials: "include", - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }) - .then(() => { - // Log out anyway - //cookies.remove("session_token") - //window.location.pathname = "/" - console.log("Should've logged out"); - removeCookie("session_token", { path: "/" }); - removeCookie("session_token", { path: "/workflows" }); - window.location.reload(); - }) - .catch((error) => { - console.log("Error in logout: ", error); - removeCookie("session_token", { path: "/" }); - window.location.reload(); - //removeCookie("session_token", {path: "/"}) - }); - }; - - const handleClickChangeOrg = (orgId) => { - // Don't really care about the logout - //name: org.name, - //orgId = "asd" - const data = { - org_id: orgId, - }; - - localStorage.setItem("getting_started_sidebar", "open"); - - fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { - 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 !== undefined && responseJson.success) { - setTimeout(() => { - window.location.reload(); - }, 2000); - alert.success( - "Successfully changed active organization - refreshing!" - ); - } else { - alert.error("Failed changing org: ", responseJson.reason); - } - }) - .catch((error) => { - console.log("error changing: ", error); - //removeCookie("session_token", {path: "/"}) - }); - }; - - // Rofl this is weird - const handleDocsHover = () => { - setDocsHoverColor(hoverColor); - }; - - const handleDocsHoverOut = () => { - setDocsHoverColor(hoverOutColor); - }; - - const handleHomeHover = () => { - setHomeHoverColor(hoverColor); - }; - - const handleHelpHover = () => { - setHelpHoverColor(hoverColor); - }; - - const handleHelpHoverOut = () => { - setHelpHoverColor(hoverOutColor); - }; - - const handleSoarHover = () => { - setSoarHoverColor(hoverColor); - }; - - const handleSoarHoverOut = () => { - setSoarHoverColor(hoverOutColor); - }; - - const handleHomeHoverOut = () => { - setHomeHoverColor(hoverOutColor); - }; - - const handleLoginHover = () => { - setLoginHoverColor(hoverColor); - }; - - const handleLoginHoverOut = () => { - setLoginHoverColor(hoverOutColor); - }; - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const chipStyle = { - backgroundColor: "#3d3f43", - height: 30, - marginRight: 5, - paddingLeft: 5, - paddingRight: 5, - height: 28, - cursor: "pointer", - borderColor: "#3d3f43", - color: "white", - }; - - const notificationWidth = 350; - const NotificationItem = (props) => { - const { data } = props; - - return ( - - {/* - {new Date(data.updated_at).toISOString()} - */} - {data.reference_url !== undefined && - data.reference_url !== null && - data.reference_url.length > 0 ? ( - - {data.title} - - ) : ( - {data.title} - )} - - {data.image !== undefined && - data.image !== null && - data.image.length > 0 ? ( - {data.title} - ) : null} - {data.description} - {/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ? - data.tags.map((tag, index) => { - return ( - { - }} - variant="outlined" - color="primary" - /> - ) - }) - : null */} - {data.read === false ? ( - - ) : null} - - ); - }; - - const notificationMenu = ( - - { - setAnchorEl(event.currentTarget); - }} - > - - - - - { - handleClose(); - }} - > - -
- - Your Notifications ({notifications.length}) - - {notifications.length > 1 ? ( - - ) : null} -
- - Notifications are made by Shuffle to help you discover issues or - improvements. - -
- {notifications.map((data, index) => { - return ; - })} -
-
- ); - - // Should be based on some path - const avatarMenu = ( - - { - setAnchorElAvatar(event.currentTarget); - }} - > - - - { - handleClose(); - }} - > - { - event.preventDefault(); - handleClose(); - }} - > - - About - - - { - event.preventDefault(); - handleClose(); - }} - > - - Get Started - - - { - event.preventDefault(); - handleClose(); - }} - > - - Use Cases - - - { - event.preventDefault(); - handleClose(); - }} - > - - Settings - - - { - event.preventDefault(); - handleClose(); - handleClickLogout(); - }} - > -  Logout - - - - ); - - // Handle top bar or something - const logoCheck = !homePage ? null : null; - //
- const loginTextBrowser = !isLoggedIn ? ( -
- - - -
- About -
- -
-
- {!isLoaded ? null : - userdata.chat_disabled === true ? null : -
- -
- } -
- - - -
- Login -
- -
-
-
-
- ) : ( -
-
- - - -
- - Workflows -
- -
- - -
- - Apps -
- -
- {/* - - -
Dashboard
- -
- */} - - -
- - Docs -
- -
- {/* - - -
- - Pricing -
- -
- */} - {/* - - -
Configure
- -
- */} -
-
- {!isLoaded ? null : - userdata.chat_disabled === true ? null : -
- -
- } -
- {avatarMenu} - {notificationMenu} - {userdata === undefined || - userdata.admin === undefined || - userdata.admin === null || - !userdata.admin ? null : ( - - - - )} - {userdata === undefined || - userdata.orgs === undefined || - userdata.orgs === null || - userdata.orgs.length <= 1 ? null : ( - - )} -
-
- ); - - //console.log("USR: ", userdata.orgs) - - const loginTextMobile = !isLoggedIn ? ( -
- - - -
- - - - - -
- -
- - -
- About -
- -
-
-
- ) : ( -
-
- - - -
- Shuffle -
- -
- - -
- Workflows -
- -
- - -
- Apps -
- -
- {/* - - -
Configure
- -
- */} -
-
-
- {avatarMenu} -
-
- ); - - // - const loadedCheck = ( -
- {loginTextBrowser} - {loginTextMobile} -
- ); - //
- return ( -
- {loadedCheck} -
- ); -}; - -export default Header; diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 2a27e91a..49115072 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -1,5 +1,6 @@ import React, {useState} from 'react'; import {BrowserView, MobileView} from "react-device-detect"; +import theme from '../theme.jsx'; import {Link} from 'react-router-dom'; import ReactGA from 'react-ga4'; @@ -45,7 +46,6 @@ const hoverOutColor = "#e8eaf6" const Header = props => { const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props; - const theme = useTheme(); const alert = useAlert() diff --git a/frontend/src/components/OrgHeader.js b/frontend/src/components/OrgHeader.js deleted file mode 100755 index 6f87bf23..00000000 --- a/frontend/src/components/OrgHeader.js +++ /dev/null @@ -1,431 +0,0 @@ -import React, { useEffect} from 'react'; -import { makeStyles } from '@mui/styles'; - -import Tooltip from '@material-ui/core/Tooltip'; -import Grid from '@material-ui/core/Grid'; -import Button from '@material-ui/core/Button'; -import TextField from '@material-ui/core/TextField'; -import Typography from '@material-ui/core/Typography'; -import { useAlert } from "react-alert"; -import IconButton from '@material-ui/core/IconButton'; -import ExpandLessIcon from '@material-ui/icons/ExpandLess'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import SaveIcon from '@material-ui/icons/Save'; - -const useStyles = makeStyles({ - notchedOutline: { - borderColor: "#f85a3e !important" - }, -}) - -const defaultImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAK4AAACuCAYAAACvDDbuAAAgAElEQVR4Xu19e9CvV1Xe3r/vnJOcBEhBSgMEBaoUK9POCOVmAuP0HwcUCNYZSUsh9xv3hGl1qNippQRE20IFEhQoBJiaKVpEgQRnhD+0QHSmRkAsxE4doBZQTs71u/zezruv6/Ksvffvkn/qd8bBfN/3XvZe+1nPevbaa+/XuxX/Tbe6C/ece8qOd5dNk3um9+7Jk/OPds49zE3uyPy4ST5zCr/y4f+sfxO4j16vHsqfmV7gpgm8Yzm/lP9+km1B9+W2zn/zzk2sDeR55ffy3enn1Lf5J/1e3bb42kX43/Do1nt9fUdpLrSbbFt8vvls+idlm/qsaJPWuNK/TfvOLU44577pnPuSc9PvT95/9szu3p9c/IH/c2oVKDbeyB8z/Yz7noNd9zzn3U+5hX+6m9wjnXM7GqXCHpbR0+PnjudGxEtBkzRo02vFtRB83rkAXPqPGD4MnmWGGa3CDqp9+pp4Bwd2dCwPzIXur6D1xaGRXaQz8nf4Kfix1/3joDXtDm0jHVbYspirbZdkj4Npcv/XO/8F59xdfv/o7zz0A1/9yxEAd4E7/by74OCke5Fb+Bvd5J7unDvGHmyCKiMzX40ByW+XQCxvyoSafgGMBT0fgTayXmA/kykr8PI1kS0JIOMPRiSJ7ctOWfuI+mcw4Uj7lO25Y0TQoyHGbKsjJbqXR5HoKijK6nuz3bPhokEX+f4956Z7nXP/8cz+w//bxR/4H00GbgJ3ep170sHC/WxgWecvUJ5AemrLA4NF2cCAa4BDSCbjYVR2RYN2ZXlAOgxZqcXUDLh9toyvSkybopAJ+KBbOpHADOEjoO1EkVYU6LB0HbPUDtaN8N7TzrmPOnfwry9631/8mcW+JnD3Xu+eu3DubZNzT1Xhu8eyxaXw47lWBIzcAW2fGSpoM1WPgzYNWqSRwqjsnSYokgMyvW5JnzWZNrQpC9tC+ZVZW1Ek9GbBQK8ZuS99mrrW1sPh7XXsTeDmqHrvYvKve+j77/8MAi9E1v6t7vnOu7c7555gaM4SuiHGOsajcV8xmeEUMSDFf4yJQLiUmpYNTg906iVCl7b6ltu4gi5lTMs6Z4RpwLQsVMP+IZaNTtYngRoNI1uOzwdYhJxtEhqKAFsdvoI0XHy/m3ZeedH7v/ZxCV7Vir1b3HP8wr3PBC1HDsog5IkJxSdDHAyBlRqBgyHwAANuI3sg3j4G+shSHASQaRlbZtDW8AmiT7b3TLJD8qACrWrwkclYg2l70qACWow5IZvQ9lHQUmeZ7p+8v/Lh7/3z36NDw6w7a9rljvvw5NwPWykOOuEono5YT8IvXQNBK5yhGnwFVoCgZYPYZgvSBtnGMMkJnR2ZrIh3Fga3mGoR9YgpL2xAjbGlBC2YTEGWJu01J3mkrwYGuFP25EF1XMFj9y6Wiyse9p+/9hXl2il78G7n3T9radomMVphpMdGUG9wAJRLYJ4Wz2CZ73QmUpkxa9jlA2fmKvNz0414Fm+Abw6b+U8D4Km+xfsb0l5E99Z+S4mApIHhaEz2bCAPSmMkaNMzW4Av94ac3vtPHdu96TG3f32evFXhuH+ru8J5f4dzrmYPVGiKN7Bfsx/syVj+C07AA3WQmlYBa4TR+bpWnhYCgjyLtN+OBrbezPdAwNcQCjq4EAsaCEAS8BF4PNIZoBKhWdo9PMO0DX5vbmGTRMi41SgyxrQlTcmsVQB+2jt/3UXv/9qdxVLTre5RBwv/m25yz+Qspe2NQWt5ZEQ5x79kMgzaoTC4DM9SuoqDvcEWSqKsNlnJHYvvQ5oWvRutiInrijdwALGBLZ003sEcMl7DSEdHoGTHyoQ4eqTxaqymzdmYuq40wrS1D5A80i+985/1u0de/LAPf+Vb4Y7917uXO+dud84fRcClIOKdVwPGQZRuNBuj6DujMHakCUCgadUsubeMS1A/vhRLBxZEgR6g8juLbUzAE1tWe8wslv9hh9HyQPknZNpEAsUQaJWPDgpttyCjsDxuTMQMwAOiUoTknNv1frrmovfd/wE/1x4cTP4jbuF+3AItwFe81ACGXm0SAC/5UTYGoSBAdwCwmZE9GAqhInmvBr8JPMmAo0zLB7G5YmdMALkTy/fOz5+NonO0VaJl0Bmyh3jDakzL2xL6JnLF8dGjk8xKDBWPtc3euY+dWT7kJX73VveMhfe/4Zy7ONOcgDpnP+a+iC1WLpZJ7RPhArLCPD7snZSVpnmGkgMZTdIrA5BIwOoHBkDLHWs1eRBMZ/Wr2FWGTcFmGQSMZYhjAInAB1C2WQIlrBGUi1JtUaJ8DHrtVIJtwyjZ6bxKjA3Q1n590y8Wl/uDW9wt08LfFgpmCCgx81FroU40KqhUvNLPYu+0NBSYiBUckjDHxtX0eCJJeqBN7cfh2WazKa/FK4MK+wGmHV/t40wbmyoBj8erYDQ5VGYC1VzFmBZpjUzEjGIjhREO5NS2A+f9v/T7t7q7nPc/qZPbYuhZeJdGL6lIMgFQ12gsJUBxwObLxP1KHujO+8nQZT15ANiOsrRcooZVXjDEyyovAzypbDKNQRifdSZiFXTI9m2JQCNBZcAcv+x7w9iF/zEACwlLZEd6ZZiFmWI7vJvu8gevd/dNk/+hPFDa02rwtXOZdZij8axlUoldwnilAYaRDKa1c5ixizxMVqfAEcUIo122RW2OCwv8/aPSotolLn5Y+nBkRWxk4aQuqGu7GOMxE5nPtQcN0DLggvGGYySjRR3L1L77/P6t/tvOuUc0gdvSZb0qL+hxtbSw/tkGma49SB3J2sl4R0SOBqNm+AYbFW9Hz0KgqIPIgWsCgHhzvaZ9rwYsjgKyzZiJMVmNVoiNsi3oWxi3BlkUwshMW7JO35mBu+dc3LmgwNvUfZVNasfH5AHVX/kOvHNBDzbXfX3jZkxnThliwSwtSswcLQDng7haFVUEWb99umAm6+5iyzA0yDbcntDmhQSwo2U5EvPzI3pWOzzDC/OaLtMm0Pm9GbhVpkD0hheX9hZwpxdWPYQ6iiqMweCgUMizB7nBYtXOeKeo0optXC1Mg8WFagMzAtV62mpKwCiC4miBdcCNSRg2aOnQNSu4Amn5MAi2LGyzoF2aaDtLflfCS2kDtBPRtBVf+cr4Dr+XgMs7Tn9CA86uNkDBr4k/AY0jQ0XRsoIdUqV9NXjLUXrywAJ8Yj2LBYrXovsrqMwI1Mhlhn6xUWq/I9tzTKsT1hNO15dNkqV5VFHAE7bTzpGep/6Afi8jXXWMBnBB2CeGXVUeaNAaYV5kD3IYLP5mFpTwlFSkx9VraWv1m+UYNqB46AL2Q6yf2lgr0HRoDX1PBTk1EliEgkO8dPhcLs+Ba9ybTRsuXmVFLI6Cwgoihh7gBcFx4JohKoli8nAJqGhczbIZQIWsynXCSA2mHQ6Don0YuHYY5HvEWsArMKs6by2Wthml9pkzeWwVilwI8KQPrdJEU/rI+xtsa/SfQwKQ1RRlKL1OLwpph6rAtUEbH5z+Xkm3LyFoOMtXwwkLBO1g8XgeSADa7COlzWqASHhKS0RU0rAwqJwyLrOGCUrD4XFKrtqusDRMefEVsfyaVR0ya17AK7GLrWXmsoxGioOKYXB4p0SjCA7ZyqpFNh3KTRG4LbZIf4vMKXa66hYKyh2ZJfNl3Poe6hhW9iCxDGkjBF6nAJwHi5EQT1hwrrBYYatOjkDM5I3FCxrJxmt9OaDs2oP2ZCqMd2jo6osL7SiJbcww1okCfu+WzNK5mXUjXjZafmAc4Lb2ZSzF1IOlafnzqIcaOw9qc0jnKhD6LMByu8Tbx3KhYiAt0ALDZ9uRqUJiAhm9UPagioTS15ZDkrqIEu0U0WBNW5+fbwDABflXjhMiXSxihDZK76x/I4+t7U3ARR1AtbRCR5mxR4TC1myarYhx7RaWcNkoU8t3KrXsMGOsqInoEJqCnA2F7x5Lz88m9m9FOMpu5LqxsksepfBuaquuAsiXYG5QT6vG3SCf1nVCMpSgGTqKHYqN/t4tCxN+sAjcvjopDvnSRiJcLOOW9fkm6LAnjw0sdryxe9Hyqnhetmyr5hSDNqGarLqxsxWkYxhEkydtyX6mz7P2aaerTVwNtENRT/Rfw6mzcJJu8BC4Bf55JEjnGsDV9bRGI1TdQZUgVVdZUUCyeSsKGJ7bOvfALD6viwsVEONOysOvBTxZTxtTekWDC5aSsixcR5weArcxEVt1qw1lxnVAW/ycSRhgU3Xhwmng1haE7BCuV2XSO/0AKn4QyMWKWKQaOYlrhArmVEL3mQOrwR0v7YX4PBlNxwT1nm+ExiwS2gfEVdCOti0afgTYluyR0my9ZVwbtNl17D1szPk6EsSF1FmYg00KuP0TCTVo1SY3GOo7RyL1QFFop2afMmzDrTgEF6eqbGSE93CBdphcT8tlUx/wehuR9V5UBL7i3rfEtPhQPQu08ffBqQoZrFZ7wHGWbGKNAxnfdeUB1dsMuCZoFTC0hGiHQnz4XGaMuN1jLOVVo8YIeMigRT/lk6Tig3bZIHcKW3pwdxZyxnTK0doDBHoJFM6erD1W9kFJwnFNC8HHcMLbB6NIa9LO5EP+obavAheE4HIvCvkpTKk/WUaqupY4ee6cAkSKrqjzOuzTkIkA1HRIw3iFaUHRi9K4lkRoRhEN2jjuKzpkM7cenpWVChnOFKuK1h/L08J0ngU+I11GMWUeu8pAi5eY/d7rYlahT/scEklbsf1JJNSmx3lv1dLyk1usMBqZsFpesEoFhhqcov1aEgJKmrktqQi8KT9Im0UYDMAeBC1tuLFVR/SNs/mcMsQBsV/WCEoTK2GAvvdDPHI6MfGuIVMcR4XvtRY/AHDJAyDTijBYrCZZU8oDYGATOCTE03BOHawJjDpoOERZuUyUORgJ0/Wa0qyGU2ikVU1rH/ckB9bapmT1TUeq8Xpa0L9evW/zFHZDcil5YGtuv5sYV64mSX6tPyPWMxqS5AFlzAIkU37USZJkWsYsEBi54ECwUiy1EA2XbRY7F0zH6NScNlkayYPVJ2J27QGaIOWRk7JrTB5Qs9UdvzpLwPCSbIBroBtEwAa4OVGc/O7rdjiENKAKfnRDRhcXJNs2JmJQG5FoaTKZZFlgIBOMaHFhtYlYn2lTaWIa4dijUdDSvnhwdBMBJyCEUqheAIWWcG25xh85tkAQMSijd4dpw4uMsknO8EPAZTWVZWJiMd78brAiVjyydboMaZxmaTLi1pLgWgcqI00LDGy2O4GvKV309nG9WGNXaWVp0ZY9DeCx72yMgraSTZ2M2hKOTpJUerSXPSi2a0QBIeQr47ZCt4yy/cxBsnUFQJywII/LwxK3k0BJkn8PU2ZEFrTAAx2NL7HGd1ttxEzDmRbd3znzoHS4x2RygYeYKnh5I3wXpkWM1ulXfk2LqBioQI2xAJ2yMwRudhfcvgHgjoR5fGJiZs32ipHWZYpZWvKAGAUyknnvINOG53Mwjy24EJAQh1w53RWEZXw/Zz7iJIp0eIiOfwY5WgbK/AOYRCvGtLT06KZSKSEa7TP6FoELdVE1TDUYQH/v8LkVWXAcfJxpYxtRiDdYkBJWKWiR90MtHs6ibUsmDdqsaRX5dEoTY8pq6Ms2iSdQBDJAKyJclWc0PYpAZrM0cy7xvQoWTcPybe78yMJHHcfQzt3XsslZ+F02Mg+DABQDp4CbeUbx0T71ThF+CM7Sfw5MxkxtJSdj4xOxvjQIoC0YaO5h6xS4w2382C4MtDzSjTOt5i+bWa0l9vAMk6ykEwwtMUOHlMCtoMjvR40IBwNyeTAM9hz00nOLkUswTE1oMnWs07WPn7dzmSx3GZkWnOaNBmx04+Wgpm2A1pJWbZbX8sDnhRTm9YwtCyj4OCAZojUnjY7ZYtGksI65MmwZ2xHg4igqgIvSMyDU9j4SEhpmTsRYgTaUBsVrbSas7IA6hu5DedoRphWar+VQWUsSpxypQNM7MizbNRiQrO02t9soWl1B0zJNrO8L6b1YeCIC5IimTZgR8oU+iLI5AS7RRqWBRgPsExPjBKKlbZTIG89lhtCpPl7XCk3cGNUmwtM7oa1ffGQ7yur3jjK7ZjU88dMTn0wWsdWD28eFBFD4r2RFkCvsYjJt2yELHEmUSsAFxmqlnhrbbTqnqLCiCNvQmAnVqYlHLnDuyBG1LV6xeJMlyeF0s4XgZNPKsZKBN5gsGt1c0RMn8zSS7wk4/DWAWFQ7ePRjNRTLAzftnSWk1l8RG4+QCLQj0kBEa8HA+ccM3OAl5Rqkv4ayBw15AItBUOcMlk+TuYrBhTvyYz/jdn7wR51bHojQxH+EYyl1n4oEzUcO/tGwR79BHEyDb5NBWr+mtsf7hdv/8/vcubtuc9PuWTsPLB6igTtQzBNsO764kAu45nSK5Sj+3GuP6P2IaM/U2l+24WyFG2ItAWttVO73O+7Yle91O0990eCwHl4mLbD/p59zp3/5ajedPSl0KdKk4XeC4AQ7lhdIQhqti0DRATu/333tkbkxMWuzxopYjHV9pi29hsciGZMkaml5EsshcDf2xP0vf86d/vfjwGXkC8d9BXkAIxxzmFD2iIOTd4Fxqw6TtkgPEpqWd6ABWlF7AAGuDCAmibV1fIHhELgPAnAx02YJUse9tzwdyAx8/jU1OeQy29utcEqQSJ1zryHAlbqPAHZM2+QHoMUBJLo7TNsqmjkE7vaAe+ZU4hQJ3Phzn2nR2M6/G1uxY4G1FATJd/PMg4/ANSZEDLhi2bFRLRU6S9JW0bmwUbT1a9ledUpw72LWuL92qHE3gG+QCkHjEuDW2S+oBBiYiJX74TIumT8C0jKPstLj78+95iiXERGsheYz0+Zbw8+t0kSRPVCgLS6MnUWfvmIAfmbcqw6BuwFuHQcuZzRddjk+gR5jWj03wsVE5b0J9LGdArh6GZezcaMAPDNqcgOmh1i8seVBdAoiN+SoUIc5BO4mmA33RuBeo7IKxt438T40EWvIA2Ns65gT/crkCcUc07iUcSVwx8O7jiujx4RWT1dgz6ZCM9igcX/N7TztMB22LoI1cKuuZHMn40gpLoDXWFxIDcd12gmwoSGa7Py51xyb9NfHI413GTCSdnw9ERyR0/un05Ql3LywRJ9XRkM/P152JAH3heuO29/4+2bgnvrla5wLeVxRy5HHQuWjENOOTMK0NIi4MWp4W9F3GaQCB67MHsQKLBTes7vUBunMQ3vHadMxSg4GvXsRvhcbGfcQuOt6YAZumJyhCfpaoMVkFtuoQM9rm+E19Z7cnPk3ALiV4eCsnjKh6NhK1f1EBkDAmx6fvPsQuOvitdxXgBvSYRJUePLMJeGaK2I5IjP8kMhagAdIa5mW78696li5fZWUVepA4MVVmTZLi+pBoxVi5NyDxSHjborcANxfmidnBLhoPqGZMr0abbw0AE8Ij0fa/AcqCfkzGL5TzYzPwB2bSeIwMF7lxfXqaoCvVVxx08cipMOOHEqFtfEbgXttzCpkxh2aiM2vHNluwyf3OLKiOYwBXHLSZwKuqMlU2obYhnQMZgGgxybAs9NNbLHOR0IXgIduJeAeaty1cev2v/x5zrhxQPPsgmjStD8s/H297IG9YVYCV8uDQoxkQcyffdV5BKbjB3WsxpYo84Bmp8ZELK3E1SGa17oX7ryrfnWzydlcDjmBU6bXx8L6d9KZB30KgVGB1chbWD4L3LCz4/a/+N/dqf9wQ5QKa+9a6JwHkV4NU16S3Y0a8GAaMUwVuCZT8swAZNnsp3lCxewUwdj8hhjeo8THqbw4gTsw7q9uJBX2P/NBt//Hdzvnd8TIGlkUGG2MyJGf2FhlzMerBozBKDeWzSmvYvATs3H5fL9w04nvuP2v/qFzB7KemYd4u2BmbAkYnoBEGw1wU5JK8NO4ziXgdlbESKfXYloCbLyF3GbajF713i0A99ydP+v2Pv0O5xdHe6dVJjvSyJEmisBJNTMm52WHKKM+06S7dggyixaF3yR8s6+kCPBmgBR2nyOXdNogB8TGxpWLwIk9ObbY5lRAlgWwgWXtiZ4/+6rz4bkKVe3wF+vMA2ScWtKWjISFuRVmyHYaybQZKNsA7kfe4Pbv+RXnFkcLVzFi6hQSxUhisaI16UhDM3Y2rdKbdGDtzxw0xqwbCSTbjh7ZBByts/0+NgVMxKztU+Raf/aV5xtTMdn5uBrGVEFrIrYFeWB++G5+71wddtV7NpIK5wJw35mAK8v3DEZMA58nG2nRT1yMQBsHqSCxOPTIZKfBnErLcuCpw5hDA0bqaRv73xRiBPhauGD3oolYm2kLb2Hgoh2kwjtaTKM+Kd+fiMUBHWDa7DlzrcLVmwN3LwGXj//IwBrn05rhr2r9qi7QbmM0mPF3+S8l6iEQgN/1o4hk2SAXmAhCS/v2xgC52qo0s3g2IQ2oaTURAMaNg1ZDEjr+xy5xK/dZIR7+nuhF677S1awDF9sB7t3vdG5nlgrFl9WuYXnuQf5ZhypsF8p69SMo48Dg77EkSA4F8f9nabaSQ5Ybx9tGEbhZygsxLRkTFuoXTgBXG0WHGkvTJb3aK5hRo03ytOYZXgBUs8a9es4qvEB57+gvzn3oDW7v0xS4IzWnKGwnPrRCaJa1pX8UGIjtKr9C0LJBRPcLSYePouJba8qLRqSLiL6pPbimRWIK4Ccv46LzglURV2wfAS7WtKU/JhOSgbS+hlhoWDaa7Etqnm0LALU14L7LuZ35bAbLIanhJSAIyw0fgTrCZhq4kTmlHTDo9WmSqG9Ivo20zXiWkofSmQ09mw/0s3aRM8+tkTkBV4NWV/J0BrZ4HfZGrYdEOqkl2hGo5rLGq+/YjHE/8ga3d/e7yuRMM7UuYuZ6UYQy9gCuac1zvBh7WkzbYPSq6dipjkUi9E6DDG1GB093okjxWS/OcEPhHWMny5m0uFDmrVpPE8Dmpb2zrzwe7s8J8OLZTLwAMOa/q3pK5Mn8d7HB/GuN8L2g1rd0KkzONgRukAoIuDKKWBMxzD5kBFJN81geFIXakvJCMgR8kagMSwyoA3o9AVd5bT/Er6tpS1daEzHm0DJn7mepcLzkcfGBxRZoeceK90jW0TMY8DmmTigjMqX+544776o73JF/tInG/VdR44Y8bp6Pyn41Io2haQNwm3laBCokQyR4CJuRd8txi5UFDVnB0D1amogJSZlgRtN8fHD4Q4NpG4sLBULhGXihpwAXAg/qWq6rasPHmTY0rCUNwt+VHha3bB243NDJ8NDvOkxWl2/HQaHfg0Ar5Yd1mLU8twAAiICCk+34Mm6RI+UBtM0GaAcWFyo+xLluJAL7s69IUmHVCvho6UQufdCqrSGmR9qGK4M7/8diS8Cd87ghHYZDIyO2sp9JOxVTVi1Q2E6biRpuhwK7B4ydKWPAG8vTctaMmjL+DoIWEh1dc7E+jRvsGRWWsJ0ixvQLf+YVxye9ImaHbl4sI/aWQaaMQ1oOVG4yrWaZ2Bu5fy3uOTvv6tu3IBVmjXuEgSWHXrL4l3CJQZG7FM0vWUKEdzXiI0X0KMqNkIUI18X266W8eqfLNORBdMrBxQW9/y05CsGOP/OKC+qP2GPqoKHywrU0LdI/NijCWEvA+53tAPeeOR12lEoXcEJ5Z+8ca9s4KKJTgu8tIOcmYwPly0j2YF15kDSz/owtGUcdQUsE8Uke6HYjohqzHwCuwbbprYoBqb6BFpWrYsZkpxo+BwMSP9LginMVtsG4u/e8y/mdo8kxrLZhRwunby9RPW/HCQsJH6nzGHXSjwQFYe4MdvV+zszxMrmZjLRt/tMcbZpRsO7EXUEe1JVXrmmzr4L5hJ6ItTIqFbjaY9TDY/+6wCtQDl8gJ4CPf2D3hzkorBBS+5zEe7fCuD/ndu95p/M7x0S9MAkjFpNNS7d4zJPdzt9/bu3T7KOwLh2Twf6f/r47+F/3haL4pKdY/MrprCpFqGSa3M6jv98decpzNDbRGJEFgtAav3DLb3/d7f3R7zq3v8+iKm3EWPUb719AZ1oNaxaoZ2UbXggmYooI63v8mZtnqWCAkcyelUhuMi0qlgGgbzgLazNcgNiGVJiB++6kcQVmgvUbNRkH++7YpVe48696q3MLq5JKPFP8eOZDv+B2P/HuWiuBpJp1ntbBgTv67Be7C69/i3M7qKa2/e75r/tf+oI7+bYbnIM7IKwNBCgSVPwwnGgnVuWusZVGbXMjEvgzN18IAjzXHqsxLZcG+Ulzco+ZEoKWh7bmh/22xrgWcO0JaiCKg3139NIr3PFNgHvnL7jdT94RMiT1H9J98XcsVG8FuJ93J992o3Nye3oam6YmDY0BTDs3NLCttB+aTFbQVvLV/VdFTnOMJsCt+kM1qK17uW8v2qtweQigHqahEL0zh9RlYMnzrn73hlkFi3E7Kbm57csDd/TSl2wZuEqjJq0IjrNaLt2xZ7/YXXD9bRsw7ufdyV+8MTJu2eWbVYsBPBQVEgCqRGg7fdXUekVMbEXRGj3PgCDjqu81IG+RjYvyIDhiU+y3S//aEwWiBefNktfM6bCf6MdE44qzd/6c25ulwlxkk/91qvZL17YOXA3aktaksixT0/JgY+Dufenz7tTMuEQqzJo2t4RzC2VCDcxqlzHQsu/NjdieXBNwVoFbG1YAaIh8rImTrl0RtPFd8pwyoIdpYXM6V+H8DYF77s7EuAW4/bLG8g2xgz139LJZKrxlfY175791u5+4vTqOsJ0KStSpZscJGndzxp13+YZV2qGUGp4PBWnQ+E6IzbJ56UFKBPAeUhdTgFuS7ubhutb+MKJTVi1NHP4ehJvcNH/KkhLjwm0HuDNwZo3ZTmGpAvCDvenopVf441dvCbggBFcCSY5MkbwVxv2CO/WLN6ZjRkcKgYxJPNS0gnxK20HBzFSSOlrrV/FbF9YK47ZOzGvSONq5gNhyYP9aaKBhGPSpoa1JhdtxVoHurxNGDyYJk2tz7OsAAB/vSURBVLOXuO0CN/afARZOYn18/49sxrh7X4rAnaXCqlvIWfYgTci4IpMMamQOGDABdkQFXH6vP3PTQ8J/K+8ugM3/oTUte6dmjPhIAMbxw/FQnWjacBiAO0/ONtW4beDmEKc02ZaAe+4TdzifsgqqOi9EgTpnrsCYgXvgjv7I5RtJhQxcfSCI1NvAoUJjVpMHHKMD2YPUYVjuGYBL8ixjZ4jFnQsFq+ZMEzeueE1TWiQJIq4pTfWzVNgUuG+Mk7NQq1AdlEwyZ7fzcCKxJeDufuIONy129A5qsxY5te5BAy4GLZNpiWFZDUkGsrqwSJDQ8Dj2CBeSbWuUVlp/ttbpxLhwwhXuIIdDhJeinQtISAPaR3uKoDzQK0nqPIfFgwFcvn08GszQfvPkbAtSYWZc7jjJbnq0+KpjAO4sFd68QTrsCyEdNp09neBmT5BUc3A9bVwJJUTIC2YGACtIkL+3gjkBdyTdRQYxOT37Liz1tMZEJ1xmMnRhWRIfa5gqr5j/ujXgzlIhTc5KDUB+U6PgYzlr3J/eWOMOAldvbJyzChsCN0iFtybgijwuIrIwbO0KrwhbMidQErTnkOnvWjZxh/ZnbnooOLbK8gyZ8mrk7EgDV9K0XAixmWSEU92efv6179pQ477R7d1N0lEJryXlxZxRhLitAfc9fOVMTcZQfncG0NIdffbl7sIb1mfcCNybuufjlqE0vufMCEVEqQqD7jaihHFAVEBe+NM3PZT7AAvd6XjJ1Jj2woIV4kAtLayN0BOx5jljs8bdMnCzCIOaVkaRWeNeNmcVbtsgj/smFxk3Lfm2zqbN6IjyLWUVtgxc5TTVc+OhlpKokFPpjY2McMojUZSv72CA52QWoo8ALvKKGi5tabBd0DIBb0mLLQF3lzJuBgViWhHi5lqFY5sC94MUuEb0YrGWSJeQDtsicK1PmI7kaMsYEaxIgDL7YdByojIiTWLfClzzXIFYexAKpUyPLNICa9NWo8XWaAXaYhTR2ZBVeKc78vQXGDWxCn3qF2c/9PNu9573kHQU0LQgdIdfHextEbg0q5GbifOgpRNbA+7N9HxcMn5pKmIc86kKX6BE6KS8yNhq6du+NwIXApJQPllNYxNGqjkJLAZPdIT7+bEexmFq5wee5fwjH08OZ7bCTz2SiKJ3ef8fuYNvfCWFwLHK+zxgoTrs2T/ljr/830SpACcdDedZLNzZD93mzt39PlEdJvpqRYEHB7iswaY8KExGL+8WzOBa7snn3WDkYf19c/70jQ9TJs8aL8CgtQQsdJ+tS1AY5Cmv6Opy/1WjdmC+YVq6sAuBnQkb+m9W/nPy3zHOh0XSR0wapsktHn6xWzz2B5PBUR/xlp/c14Nv/E+3/NZfiAHtMG0miwLcf7d2OmyenJ18682yHjcCqdQd0H4hYjDkgULViDwgTtuM0s5h4M5LrIVarQHBQr28r/wHuF+xSAVFeS0I0ZwOeEhlK4MwgtR2FNk4pUNJupqW31suD1tn5AilvliGp3aZdz6UMuU2KGoT03VbAu6pt97sptOnnMulIDMXhNEfmYjNrZJVgQZexNYkKAnNRRcWzmOqmDJu2GqjcpmlIVX/GFVEbKwa8kOsL/OcdQvwbEeGNlBse1sbccdCwG3v5I0mJO+WABU/g3DWnp3nMYISgbRtTseFydlmjHvqLTe7aS4kX+Ro58O2myI/ZX/LH7g0iBaZK8wkCyBbrfE1SYZLAdyyKmbStAWKKHrCbU3gSS1Iwm+hQQEMaDgRtoutbNCGx4+cLsNHrOSRi0Pn9nQZNQ0kG8d+2aSbyEGA9F5JBFsE7vLM6Tj0wymvyLQQ3K0QX8IikITMYREG+OnvgXEZ05bWIMq3j/WpTGaFCg7arPO4g7ZFeQQfB20FFAItZkedp+2UNBZ/bLN5LcgxnE+xEWpfo7yQ3r8/55G3wbivcMszJ/mh2tLZGEJr7UHdrNifTEWUA8IJg9o/LkrWCvvTN/4tHuFb+tDwJh5+EXA7tQeQxfSg4vNX03UtUORPcaDySKPgw1xybEUVYTtuWIMIGNMMgnYOyftzHvlFG0uFk295RZQKQ5qWfCTRlGW5Q9LJUZTsEEF6lC5wD1IhAreGUmDgTUsTxWDzAe3sps0mhTXDuWf2RGLdc7zyE2Nbx5jW3Gbf1X2tXcIGAAJwZ8Z902ZZhRWBq+RBq28getfLB2RTmtNoTkrAfXBWxDTLRpiBjX9wCZgP2Nhp19rbY6cNYMBdqsbEwRogoemVUw4yGYvO5YeGNJuBe+nl7sIbtw1c5KTcfpHpLEnIJ2M4cqH79fNaW4n8qRuyVLBZi0646MCYX8UJHQOdRYPY1X0MSPnqmK5p3FvCCARu47yEpr4jjhFFespzG4PYqz0IjRxZrROyaX7trHEvvdw9ZKvARRmAdfO087hlIxGjhgpndu4UlinWCefpUf7UDQ8Hw6+32kTT1Y41WZrkgWvTpcbphIrQcMurka6Vnj7OtDGixfvbkz09sLgQSAMtRxvtbBS4Y5ovtHcrwL3XBY0753HpsRcFEegAv4GJWJJ1Cp+43JWhOyaR+1EPALc/O69s1piIJX1DNWLpCA4zbLUrhwndeStMcaeK9wk2E5OnAiZJAErTin6mgWX61wzvQr4U3bfaEnO5LdZGuWl/PtfhhZsx7hcTcOmBIAC0tfed3djFLiPRmzt3G1MKk9MgcEd1aUp5FbShIzQbbFRGx2LbwRAPswdtXcXDToNVBPhVuGrJAwIKrWn7bJuLnIJ5Z+BetiFwv3SvO3kbyirIxQUEWDGOsW98S1fTmTm4cQTHhBHezKTCUPZglGUB5ff27TfrIpA8qMbralriFONMa7ElYfciLwZmySZwkbYUAyvD7IMGXJny6kc4Kn/gbmHm3Qg/0XZas9p2qcCNoCJ6A7GsxZYogzBSMENYJtCI8ZGQrD5Vz7g8MPeHgfsMI8X+lz9iTZt/S+ZncpZtHB00lt1QOVWk97cB3CIV8p4zuRo2NvunCwt1REc20/Lns3qTKteSmTngE3DlZAwclQ71YZoACRT0vU47gK1pG2F7marDMjHmdhjfHGPNbNQf11qKaCwO0NGJF2CWhsbO1VjxXZbEEoXkz7ncPeTmDbbuBOC+Mi1AiOyBOd5AvxLD1v8cm8TJoaPRsHUclz91wyPA19NX1LTp7XqyMhY+zexB03gLt/P3nuUWj3qiqsfVbNrKO1K1icJY7yw0cr/Z3ngNGlTK3mzQtAhOD8mctnRHvv8fuvN+9PK1tw7tAeAW3+8dElO8mcum0uyhVOBIsQ0niowxf+r6R5Bxrg9KGx7SL+SA6lpauL0dpz/YKALQVrliHqocP15y/rXvcEef8eMEuNZo///6+zknip1tpMczcB8gjFvm1D3QBS9EgEUTTD4PiVegKjIRyTAJhM8czM8gwK1hkdODMRlT4UHOAC2D5t9bjcfF19Qxwgx0seOOX/ef3NGnP39kjA6vARYowD09H3qX9G1vAl1ChwRuf4IZNVej9LExh4mvjRGcABeB1gKenIiN3is7ZkzEWjpwbnzu3CFwN3bGCtwzjT2FGJDG5Nb8nkQsfB2v16WdK6qEOJU/df338DlL+cmQB0ysjeZpqdbtZg6KzMqarzY8d2feObBwx69/xyHjbgDfCNxX15WzFuMJQuGXdspRy8fJwcQut589kF+HahYKcMt9kPHQ/rCkVZjhOnvEkr7hE5X0gEaICsAtuZKUZzwE7gaQjbcG4N726phVYJkYnBExT3TshPiYo1XPjCnDwkpC45beYUkZgNsG7fyEWgSeZ3XQak1RbyWZBzRtQTrZanMI3O0CNwIrwUjOVxBJjeZ4O9+DMCN8axK3cP5klgoDTFsEsjRZR5fm7e9aFyUvszw2r6aEIxNFwcchcB8M4Or9dDwnzjdSDkymmh/1E7KTqoaQ4VBEWHPN/uR1WeOi8BAuzJE6PXcke0BmnKnaB8sDOzyE64s2AitOM3Cvm9Nhh1mFdRFcpEKpDuMTsUi/YvKd5V5Dk4ZLzOq+fvbBXowqkX/yJ697JCBClKcFIOukTnKJGvWkYuSBe+MbQWndfO9iBu7bD4G7Lmqpxj192ihr7JcX2vn7+cSLxlctgSwpMlQhkhBX+hsGLqmuMjVtL9/XqqfV0qJoK5pBiAlxWZqYPPYQuBtANt4aGfc1sB63eeAgmTixRoTBG015oWiL7tWgDXdqxm1sIS/UiXK86+ZpReojhRnItLRO9hC4WwDuH8asQpYKhFBUuktpPYyBsXMVNGj1uXQzYOf98hK48b0EuPZO3Mh86V+Lac2Om/fy4vFinIHdrmFydigVNkHv3hclcOVqGAFYayKW/ja+L1Bo6QCwsXWD3F8O3NIAROP9ukxW6Kws2ikCL4YZAG1oXlqAeMbzNhm7v9H3BuC+ec7jzmWNqxe8yPmKwnY4WD/pXJH2yj+GSrgBTcuW/CvjysM6UFmjLQ/Ce9MXCWHKC6bLonOklEVKIIxuZ5lv3UmMewjcdb1vBu6JN7/aOQjcNN4dps27eHNMZYeENOTFKhMxtO3fn7z2UbF2wUpvNEBX1IN5jb24QCdh8TnDoI2zVb9wFwSpcAjczYD7mnIgSJWE/ZRVwGRvi7qBKRu0AgcN6RmBW3K1eMWEG0auIxuyIkposB+j5gXjQf3W2bJop3ES5uHR3h37x1e6nSc9DZyYWFsMI4BigtbQo0hDrjdf0LlPvLI4cvl9inrm81GbV3inX7j9//1Vd/Y33++m3V3yMMS0+rm5UgtiwyLBzGOQ6FT2IJlE6u48OYvAxVvBe3WZ4ECN0JEeS5cjkcaZFgt/ckynFdIMbVXb2dDUJaQYgOjaZ5UyPvAOfUhfncwa381lDjCSsmShdhC0LaYtDcA2w6fao5SX4SxpTBLjVhasHlQYLzelLAdGXYruyUrHSJUkULMshZWnVYRinPAnAbv2hk8UbdBhHbbWz85Qm8RCrmIQpd0YGOV7UPQiX0EKl4/pUnMi1tKzaexKmGfjI9+7yvjb8oAHRm5L/8C1fycMNWvzgKfKWR4AvIBeAnprGRcydWlw9JcsQWqvOCAAw/L+dfZClft72Q3ECLmKDYCuGYmSqXofCmGDJPaIhed3nCq2obC2crBGiI9OKckKOTtxIIIALC2a516QI/bpe1IxeQFuk+IZ2o3UyUCVVxMU7WqjYSMn8Ofrc8ubmqwYuKG3ufuDY4NybYX8poE2ugwmwfTLRjpKvXtkN25+S2FDnTPXJJADcRFJNfUzuoVcO23SoqLbTdCGHsOT0ZPzBMYtT+zWD4A0Wbm5X0wcL0XhV3upmcxuhbOWntUTxTqQPZbtMVFiI9W0DIyATJzHLhFEaVZiE/Jg9n0OOjfp2qUCipFATx6UXSfCASm0KQYEPNdg2kju1KnyL4gNI3DbIZoc1oAFdy97UHWIBC3SZMjAeBCZRiRM25cGIJxB8LbbV8ZuKC3Ebcfw0vrMKGHbsQOpcd/GisDB+LZ2+7YcepoZE+3kbDOtiqyl/7xt/oFrMuMaHpVutLeQG3oujWr3fFrGJo0Dz1i4lNKlhkXNeiOatqdnRdilrNLdxo21cHhEAqxyNPb8GqWKmjOjnLBLKnipcon0A4YHfn/zIG1jPMpjIRl25YFm2hTNBJE7/8A1F4Mu0A5IQU4eMTMNt3rNPLCnji4uoHc1WI9IgzHACjYKN422TTNZ6/zWGu/sKBW/I0b/YYek8qACo00YuHjfdnjZEnQKOLtGyLISfQygWcUy7D50OqchXwFwA0OFlTyac1Ne29XDuUkjmtZyDgO0gnrW0sPBwAi0CaCNMBj+1DrzoTJibmlRS6FH6SMh0NnYSNZJWOWHAflCSkohm7dCvLkiJhwFMKpdq4JLE3larvv8aMv0XgFcoi9N3TaWdomdGGWzkYJlwnjJ8Guddg21rGA9hShp1JEMALGlpDOlaS0mlCmvEdnTOenSiJDFX/KsnaVIUfvqPsfqazjXrKKKkIfx7zrKS7MFvihZBSoVClgNBoThgQ9q1rRmPa0AhQYf8jwNWjOP3FvNCr1HTsWYLDElCPOwQF4OrL4vPBDmaVV/p/q1PLptCjGtHPD4s8nkHabNk2x+f5vh87W4ymtuSv7yH0+X1qBpSB4gYPOhiPMd/oFrHl0vWW3XQnKI2jH+Lsq2yhNDu8eq7PHgFPzVsGzWRZRLWlGAi63ctxrq6945khi326bat+I3xCog4oJBs0CbpKwUoYKQrrfbrH0kUsEAbx+ahAlbpTsZBqwon3CZ+8aBS8IvoujxlNlIyqt2ooLdCpc6TGvQosUP9LyR7MEoy7ZBy9o4mO6i0qrYBQJvhGkHGDqFaO0U6Pn8d7V9KLdv1x4Qh4yRv6TMjHrt1H9JwJVxm2wLBpPuww/3rjbRWSmcEYkC70NhhWkmJA2yyxuhqjCCpemlfBGSKXs/BG26l7e7hFXmzEN9Q9KuHeJrFBqRFlj64AnqGhOxVo2FOPC5sO4sFXDFTnsJN8X69P8MYIwUvMBGE6OX2DdUZKO1aRl4EAkUKAD4WqG2OlSRFJxpUXi0IovczTwAPMFG8Q5iJ9Y/g3zUSeDyvei+egAdj84StMiZAVEwsZoB0d506R+4+jHhNjWGAyFqlcWF+I5sWrAOTVi1yM1STs9BW7A8NBFbLRLQd0cYGA5TjKYHJ7bPp7QXHdoUDpkQBSmv3kocfzfW3C2nJJqRj33oS0yGln/1PzNLmSWmtKsEP9mrY5OovaztXDHKtaJrAO5KoE0fr4olZauBYizMa4/nHYdhVqRTwjX6OPtsuB7T9pwWOFkBfH6HtbgA5AH/1YA2Je/v34s0ewMUzKnKvRGz4X87KS9hG9i+YizM5vYnFeq7/YnEuIzyjeR69bh89Xi9KvQ6jWRwYLAd4hArVO2G5Mto+G2cd9UDbSt7oPoL5AGyidLrnAVjnwf6BhcXkHTRNsfFMgAHUB6OMm0FZjUDllYAuI0kN9NDI9mD+NLciLi1jRReIBC0PHY4hIa2ZV+p+B5e7QM6jMVUNLC19oCRgAJd/Kt50Am7WbeD6gI1uAz0oI2t/rfunQtmwuG2vGetZVyTafPOX4O02gxdHaACN4dHFiq0J8cHj6SV8ApOWEk2NFQ1DDvSPw50oy6Cecfgd3vRztFWlRt/B2A8xbTZkJIJQS2tKrkUoAPSpUaw/kdCYqTkJMJY2nDKONaWdLFTXuxxQ5q2UevLQnW1iwYu8vgm06bOKQ8ywhnybK4j9P63XmV/eaZRFyFOHIyjIcGxWlqIRBE3rZCnpWGgAKNhu+gwDWnAUaIK3MOjW5NYk2mj45knJrIP9RrSQmLJkEH1cEN6Q9t5/YmrHwu+uiPYsgkMrEEboSK1jmuXOKDohHNDwCsmGK+LoObJA6sCjXo+b0cBbm8ZV9iO2iW+UzKm1nTr1tKaac6OHi5jAYGW7Fz+xp2q2pHay1pcWD0K5LEDwK0PC40wQWtV9MuVFOQ5PARV+wCQ9lJe4eYx6aJZdmQSRtpKhXMALP+bFX5nPZt7phzaYrz0ewpaBQqTLfO4rTYWNYokDLDnr1lLC48oWC+K0MDsT1z1WOFXtdHRUKOgiIPIoz7SR5hRIOP1QmgIg6PtE4zZ+kq3ofmyt+McrdCDpEMZuNU2I1VeiI0szTkie9S9Kl1YzK1OJWqDlvWLhbNGtBRjq0jAZPT4TAHcCrT6XCN7QAaXhnkOQAlcBdry0eJ8XzwkZORbEtlCxooYawgIZ0O6DxveXFxQepO3DYfudkQaZOiMnWiU1m7cFkuPTMSgZpbRBxEWQfSwZscROHQ2Mi6XB/E06YZm7IGicYJNNGxRKoMrdmhw12daXR2FmMwA7cBELKJI1tKO1THTnQv9KKSjSO5bm0C0/MEZlb48yPKI+cNgLTeTiL3oKivEMnDDfeXMA6QbLS8aKQLnTJvDAjVu/O9+agfX0iaF0tPDvS8agvBU5QHcQp6YTh4XhTTtWN9wcXznKKs0dmZtc4NlC49AsJGJWBgg4SiWxlcgrHUvhpSs8tp4pjzbzp+46pKI2fIyYxm3AQoVzpBGJBetVYeL9SwEDpqE5ZOyNYvJAeHRh8qg/KHoGvSkM4O6gzwQLUZpRCBcoM2lT8jHkE+PMyZDY8FqU8K9ot5hlGll9snQtC1p0LBLQbOqW/BBKuy5yR+J/VsNtNqx2qmdzLRUv+dQAyoqRE7SOqwjhb2WdjM/oEKBZ0mDOWkhC2aUVmeZjbYu5WE6a1JoS0r3xWjCscKNqO2N+UUB7np1B2U+wirR1gCt6p98hlkhtu9PXPW4b7tpekRT0zKkNcIF8h7GtMjIIzNsJF0QYPXzYzgxjFqkifH3/pFIOfIV4Cq2Y4wn2iekCTNfb3m6PHcctKSxkSda8kA8H0bVAjwLtLy/MdLSVrD5jviDPMaUveM7/q+vvOSPvVs8BbKgYXTaibJo0GC8ONlDE6yG0Zk3GsU8LO6DZ/U+WWSyVXw5XhEjg0HkFQcFmg+g/sdnKUD1loDTuJgLEx0mGzpdxgSu7EefaSuMUKQyMJBsC4nATX/iT1x5ya9PbvFPOHA1C+Iw3xggAWRFxpss4w5M4rqrRg0mDm1dhuR9Z39ZjAR9aQAijZglF/t3J5irTGKN6KgGo69p4fg39ojRgvaIJp8OLGQCGy9RJ80OAR/Gxt3lT1z5fbdObnqzc24nGs8GYx98yKMQ23aYdorZ3FW2tzOSaR7rn9+NBrWeeaAjEGZaFZTW3tHRtn3tHxofdG/pXyX0FGH4OKI8eLxFRgK2JG9Fq1aUUwDSUWggihwsJ/cv/Hdf/vhnOr/8qHPu4hgf5ek0cjWsYaTc01KUk79LSJiLe2m6gwLeAm1nwNLIdpmWHLNJwVls2tuNG2yEKrySXTqSiebM6/uxw4PsCGcuTjRCI0rHRPZr11PX4VxNHkA93LJLrv6DbMGwMV/5zQO38yL/zZf+gwuPH/nrDzvnf0Ks1zIaV1oDNYTpkrZ2yTgrnzRkz1uvYGbsXIDarvzKIJUhYC0WHFlcwJqe+DYZprYmDu3s1SIb1VosxFsSofzekBalpfZWm6ipetucgGRKztfbfl9OsHHut/z+4qdDS7/78u99qXP+Pc65Yxz0sSNDoE254AACMRELv1tb044zLawuY6Gbgza2Nf8z9ogpLSCWcY1CccaWBBiQjdQ7BIBSWM6/tccDkYVle1lPq+/lZx5YoENLzOJawzGi5VEtLopA4Xf7y+Xy2ks+8bvvCz+duO5Jj5x2z/5X5/xl5WEEwbbXElAVFEiwDyx1lo6tybRVV/FwaUx0FPHMxhs6gC5OxBjghx3SIAEGWgC8BFrYZnBvNUBiP4tljfcqp8g4MBh/nJCAY8C5iARt/dk7/wd+37/w4k996i/Lb7/7su/7p5N3t3vnLmBbnLmO0uHNMF69cN08rel1LCjEMArYoLPSZ8sDFOLn5wNduwJo+8BDfYjRqzoKsom+rxBNC7Qj42bZlkYpJg9J9Oo45CDgaQXb6YMDd+3jPnnPh1iPv37dYy64cPfYrzjnXtY3MtZk6r6B1I5Re4DBSIuByASQIVmFbhQCE2u2CmaE5t460ypjaYdpzrAb4Xc8T8ulU462SkJpQJQQr1ROpwA/v6OpaUHfls5/cLFz/vWP+djH5s9gcjF64p8/8UnLxXJG9FOZZGANbzMhCzWtwWnJgx6TldoKCUo7zJSIlw6fy8u4ZmgMI1KlQR2gxrI29SCyvq4Y07Jnjm6TONpARj0hy1jfeCERkU60YCZlqFuTqdBhrJlRJVlvgSk0hETGYXKMfb/3YOFe8rjfuvvPBKdXi//VlY9/rp+m9zq3eEL4bRO03GOLPQeAZzItrgnlH97oz7Dh+n3oymD2II64XFxQg5mAgR1GsfTAwgkFhXKqBuCH5IHB0uVe5gHtmmgOPBSBgYQhwBVREnhqtqn/2r7zV37vb3/qM/Qe6FLffdkTnz/56e1uchG81OMZmEX6I4xU6TDxdjmwjVpaajz53sJIwp8kI4msBgPQQD1tbAKo9OpJnzSaYwdN48kKH1Bh+x7wFI3JzIEGUxxO2pb26TJqeMzic/7MzLgQ8AJT5B33T37nlZd8/JMfl3axYoH7q5c94TneTb/knH8qWk0bK020JhNrFIH3wowxqNUIMXOg9JsaCSYPkr2xpmfGFO9XA9Rhy1iW2JM+OQbWsynCYyGTCdACeaGYlhCDciBgf72ShhyDE5mKIsz+DOz3Huy4Wx73sbt/DzmzCdz54u++/Ik/sJzcG72bLnfOX0B2Log1/E7Kiw1ae7WmNrKChXYWAg+AgodplO4iRi73V2nQ1qVigIDTMFA0QFsYT7Fley5RSQp981fWHuhJWPwNjZhGuE+AVc0bmoQlO1kH62HQnnaT//WlX77pkt/+9FdwBMKFnOzasLK2OPmCaXKvnpz/Ye/cUcbspm6JhRWZIzbZiavDGQqhIPTmy1bMHnBjDUzGoESAQNDnma37OabmMmmZiCX/1cDlQ29p1M7ignbIzBfRhKqQSIwRd/hdN/nPueX0Tnfs+G/k7MHawM03PvDSv/uofeee57z/Se+mp03O/W3n/E63AJywmQIE8wAGxmKAsXwfN0h5ZW8ixt4PFhd65YUEsCoSaIrSS+jDs3YOvPCuXi0t6xvhD1R0bk2m4a6KyqIqOrIBbh8TmoB94Jz/lp/856Zp+V+Wx93vXPLRT3/bAiv9fVMqoAcEBvYP/NByWlzm3OJZzk1Pds5f7Nx0kXPuCF7qNHbiio6q9yWDKr0IBkXei2sPLI9HW25GmXbFLfmFiRIALBsA+VG6rYEGvrWAn6/8ySxNRCwtnmmtpqVFk8i4lZC8c3uTcw+45fQN5xdfnvzyD5bTkc8e3Xf3XfypT50aAWy+5v8BUrIHNHvQF7oAAAAASUVORK5CYII=" - -const OrgHeader = (props) => { - const {userdata, selectedOrganization, setSelectedOrganization, globalUrl} = props - - const theme = useTheme() - const alert = useAlert() - const classes = useStyles() - - var upload = "" - const defaultBranch = "master" - const [orgName, setOrgName] = React.useState(selectedOrganization.name) - const [orgDescription, setOrgDescription] = React.useState(selectedOrganization.description) - const [appDownloadUrl, setAppDownloadUrl] = React.useState(selectedOrganization.defaults === undefined ? "https://github.com/frikky/shuffle-apps" : selectedOrganization.defaults.app_download_repo === undefined || selectedOrganization.defaults.app_download_repo.length === 0 ? "https://github.com/frikky/shuffle-apps" : selectedOrganization.defaults.app_download_repo) - const [appDownloadBranch, setAppDownloadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.app_download_branch === undefined || selectedOrganization.defaults.app_download_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.app_download_branch) - const [workflowDownloadUrl, setWorkflowDownloadUrl] = React.useState(selectedOrganization.defaults === undefined ? "https://github.com/frikky/shuffle-apps" : selectedOrganization.defaults.workflow_download_repo === undefined || selectedOrganization.defaults.workflow_download_repo.length === 0 ? "https://github.com/frikky/shuffle-workflows" : selectedOrganization.defaults.workflow_download_repo) - const [workflowDownloadBranch, setWorkflowDownloadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.workflow_download_branch === undefined || selectedOrganization.defaults.workflow_download_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.workflow_download_branch) - const [ssoEntrypoint, setSsoEntrypoint] = React.useState(selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_entrypoint === undefined || selectedOrganization.sso_config.sso_entrypoint.length === 0 ? "" : selectedOrganization.sso_config.sso_entrypoint) - const [ssoCertificate, setSsoCertificate] = React.useState(selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_certificate === undefined || selectedOrganization.sso_config.sso_certificate.length === 0 ? "" : selectedOrganization.sso_config.sso_certificate) - - const [file, setFile] = React.useState("") - const [fileBase64, setFileBase64] = React.useState(selectedOrganization.image) - const [expanded, setExpanded] = React.useState(false) - - if (file !== "") { - const img = document.getElementById('logo') - var canvas = document.createElement('canvas') - canvas.width = 174 - canvas.height = 174 - var ctx = canvas.getContext('2d') - - img.onload = function() { - // img, x, y, width, height - //ctx.drawImage(img, 174, 174) - //console.log("IMG natural: ", img.naturalWidth, img.naturalHeight) - //ctx.drawImage(img, 0, 0, 174, 174) - ctx.drawImage(img, - 0, 0, img.width, img.height, - 0, 0, canvas.width, canvas.height - ) - - const canvasUrl = canvas.toDataURL() - if (canvasUrl !== fileBase64) { - setFileBase64(canvasUrl) - selectedOrganization.image = canvasUrl - setSelectedOrganization(selectedOrganization) - } - } - } - - const handleEditOrg = (name, description, orgId, image, defaults, sso_config) => { - const data = { - "name": name, - "description": description, - "org_id": orgId, - "image": image, - "defaults": defaults, - "sso_config": sso_config, - } - - const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; - 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 updating org: ", responseJson.reason) - } else { - alert.success("Successfully edited org!") - } - }), - ) - .catch(error => { - alert.error("Err: " + error.toString()) - }); - } - - var image = "" - const editHeaderImage = (event) => { - const file = event.target.value - const actualFile = event.target.files[0] - const fileObject = URL.createObjectURL(actualFile) - setFile(fileObject) - } - - console.log("USER: ", userdata) - const orgSaveButton = - - - - - var imageData = file.length > 0 ? file : fileBase64 - imageData = imageData === undefined || imageData.length === 0 ? defaultImage : imageData - const imageInfo = - return ( -
-
- -
0 ? null : "1px solid #f85a3e", cursor: "pointer", backgroundColor: imageData !== undefined && imageData.length > 0 ? null : theme.palette.inputColor, maxWidth: 174, maxHeight: 174}} onClick={() => {upload.click()}}> - upload = ref} onChange={editHeaderImage} /> - {imageInfo} -
-
-
-
- Name - { - const invalid = ["#", ":", "."] - for (var key in invalid) { - if (e.target.value.includes(invalid[key])) { - alert.error("Can't use "+invalid[key]+" in name") - return - } - } - - if (e.target.value.length > 100) { - alert.error("Choose a shorter name.") - return - } - - setOrgName(e.target.value) - }} - color="primary" - InputProps={{ - style:{ - color: "white", - height: "50px", - fontSize: "1em", - }, - classes: { - notchedOutline: classes.notchedOutline, - }, - }} - /> -
- Description -
- { - setOrgDescription(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style:{ - color: "white", - }, - }} - /> -
- {orgSaveButton} -
-
-
-
-
- { - setExpanded(!expanded) - }}> - {expanded ? - - : - - } - - {expanded ? - - - - - App Download URL - - { - setAppDownloadUrl(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style:{ - color: "white", - }, - }} - /> - - - - - - App Download Branch - - { - setAppDownloadBranch(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style:{ - color: "white", - }, - }} - /> - - - - - - Workflow Download URL - - { - setWorkflowDownloadUrl(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style:{ - color: "white", - }, - }} - /> - - - - - - Workflow Download Branch - - { - setWorkflowDownloadBranch(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style:{ - color: "white", - }, - }} - /> - - - - - - SSO Entrypoint (IdP) - - 0} - id="outlined-with-placeholder" - margin="normal" - variant="outlined" - placeholder="The entrypoint URL from your provider" - value={ssoEntrypoint} - onChange={e => { - setSsoEntrypoint(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style:{ - color: "white", - }, - }} - /> - - - - - - SSO Certificate (X509) - - { - setSsoCertificate(e.target.value) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style:{ - color: "white", - }, - }} - /> - - - {/* - - {expanded ? - - : - - } - - */} - - : - null - } -
-
- ) -} - -export default OrgHeader diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 6d0d6185..45ad7dbd 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useLayoutEffect } from "react"; import { makeStyles, createStyles } from "@mui/styles"; +import theme from '../theme.jsx'; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; @@ -8,7 +9,6 @@ import { sortByKey } from "../views/AngularWorkflow.jsx"; //import NestedMenuItem from "material-ui-nested-menu-item-v5"; import { NestedMenuItem } from "mui-nested-menu"; import { useAlert } from "react-alert"; -import theme from '../theme.jsx'; import { ButtonGroup, @@ -170,7 +170,6 @@ const ParsedAction = (props) => { //setExpansionModalOpen, } = props; - //const theme = useTheme(); const classes = useStyles(); const alert = useAlert() diff --git a/frontend/src/views/AdminSetup.jsx b/frontend/src/views/AdminSetup.jsx index 785b0954..f8f3d747 100755 --- a/frontend/src/views/AdminSetup.jsx +++ b/frontend/src/views/AdminSetup.jsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-multi-comp */ import React, { useState } from "react"; -import { makeStyles } from "@mui/material/styles"; +import { makeStyles } from "@mui/styles"; import { CircularProgress, diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 9d7e2700..8086e1e7 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -1,6 +1,7 @@ import React, { useEffect } from "react"; import { useInterval } from "react-powerhooks"; +import theme from '../theme.jsx'; import { IconButton, @@ -270,7 +271,6 @@ const Apps = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata } = props; //const [workflows, setWorkflows] = React.useState([]); - const theme = useTheme(); const baseRepository = "https://github.com/frikky/shuffle-apps"; const alert = useAlert(); let navigate = useNavigate(); diff --git a/frontend/src/views/Contact.jsx b/frontend/src/views/Contact.jsx index 5a34382c..afd609a4 100755 --- a/frontend/src/views/Contact.jsx +++ b/frontend/src/views/Contact.jsx @@ -1,12 +1,12 @@ import React, { useState } from 'react'; import { BrowserView, MobileView } from "react-device-detect"; +import theme from '../theme.jsx'; import Paper from '@material-ui/core/Paper'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; -import { useTheme } from '@material-ui/core/styles'; const bodyDivStyle = { margin: "auto", @@ -19,8 +19,6 @@ const bodyDivStyle = { const Contact = (props) => { const { globalUrl, isLoaded } = props; - const theme = useTheme(); - const boxStyle = { flex: "1", marginLeft: "10px", diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index 9af90f64..dfe028fa 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import { useInterval } from "react-powerhooks"; import AppFramework from "../components/AppFramework.jsx"; -import { makeStyles, useTheme } from "@mui/material/styles"; +import { makeStyles, useTheme } from "@mui/styles"; // nodejs library that concatenates classes import classNames from "classnames"; import theme from '../theme.jsx'; diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index bf32bc96..ea5354ea 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -4,6 +4,7 @@ import ReactMarkdown from "react-markdown"; import { BrowserView, MobileView } from "react-device-detect"; import { useParams, useNavigate, Link } from "react-router-dom"; import { isMobile } from "react-device-detect"; +import theme from '../theme.jsx'; import { Grid, @@ -56,7 +57,6 @@ const Docs = (defaultprops) => { const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; let navigate = useNavigate(); - const theme = useTheme(); // Quickfix for react router 5 -> 6 const params = useParams(); diff --git a/frontend/src/views/GettingStarted.jsx b/frontend/src/views/GettingStarted.jsx index 7160a635..9324e159 100644 --- a/frontend/src/views/GettingStarted.jsx +++ b/frontend/src/views/GettingStarted.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useContext } from "react"; -import { makeStyles } from "@mui/material/styles"; +import { makeStyles } from "@mui/styles"; import ReactGA from 'react-ga4'; import SecurityFramework from '../components/SecurityFramework.jsx'; diff --git a/frontend/src/views/Introduction.jsx b/frontend/src/views/Introduction.jsx index a0955bc9..e94295f3 100755 --- a/frontend/src/views/Introduction.jsx +++ b/frontend/src/views/Introduction.jsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; +import theme from '../theme.jsx'; import Grid from "@material-ui/core/Grid"; import Card from "@material-ui/core/Card"; @@ -12,7 +13,6 @@ import Button from "@material-ui/core/Button"; const Workflows = (defaultprops) => { const { globalUrl, isLoggedIn, isLoaded } = defaultprops; - const theme = useTheme(); const params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) props.match = {} diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx index 52a6a6a4..bbac998b 100755 --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; +import theme from '../theme.jsx'; import { Grid, Typography, @@ -15,7 +16,6 @@ import detectEthereumProvider from "@metamask/detect-provider"; const Settings = (props) => { const { globalUrl, isLoaded, userdata, setUserData } = props; - const theme = useTheme(); const alert = useAlert(); let navigate = useNavigate(); From 55b62d930a47334b83dfda582cd394f006dbb2f7 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 12 Jul 2023 16:40:21 +0200 Subject: [PATCH 006/160] Build ARM & AMD --- .github/workflows/dockerbuild.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 4ef90c7a..bdc7b0d6 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -35,12 +35,14 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: 'amd64,arm64,arm' + - name: Login to DockerHub uses: docker/login-action@v2 with: From b9a25ee5371940bb52c8da3206e8ccb75e6d2162 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 13 Jul 2023 01:53:27 +0200 Subject: [PATCH 007/160] Fixed most of the new MUI problems. Missing /admin and other button disabled issues --- backend/go-app/README.md | 20 + backend/go-app/main.go | 3 +- frontend/package.json | 41 +- frontend/src/App.jsx | 151 +----- frontend/src/components/ConfigureWorkflow.jsx | 2 +- frontend/src/components/Dropzone.jsx | 10 +- frontend/src/components/Header.jsx | 6 +- frontend/src/components/Oauth2Auth.jsx | 2 +- frontend/src/components/OrgHeader.jsx | 1 - frontend/src/components/ParsedAction.jsx | 2 +- frontend/src/components/Searchfield.jsx | 8 +- frontend/src/components/ShuffleCodeEditor.jsx | 8 +- frontend/src/components/WelcomeForm2.jsx | 14 +- frontend/src/defaultCytoscapeStyle.js | 432 ------------------ frontend/src/theme.js | 102 ----- frontend/src/theme.jsx | 46 +- frontend/src/views/Admin.jsx | 56 ++- frontend/src/views/AngularWorkflow.jsx | 22 +- frontend/src/views/Apps.jsx | 26 +- frontend/src/views/Dashboard.jsx | 2 +- frontend/src/views/DashboardViews.jsx | 2 +- frontend/src/views/SettingsPage.jsx | 192 -------- frontend/src/views/Welcome.jsx | 10 +- frontend/src/views/Workflows.jsx | 24 +- functions/onprem/orborus/build.sh | 2 +- functions/onprem/worker/build.sh | 2 +- 26 files changed, 175 insertions(+), 1011 deletions(-) create mode 100644 backend/go-app/README.md delete mode 100755 frontend/src/defaultCytoscapeStyle.js delete mode 100755 frontend/src/theme.js diff --git a/backend/go-app/README.md b/backend/go-app/README.md new file mode 100644 index 00000000..158fb213 --- /dev/null +++ b/backend/go-app/README.md @@ -0,0 +1,20 @@ +# Run +go run main.go walkoff.go docker.go + +## Modify +- Make sure it's connected with the latest version of the shuffle-shared library, which is used to get resources from Shuffle + +## Database +- The database is Opensearch and can be modified with the SHUFFLE_OPENSEARCH_URL environment variable. See .env in the root directory for more. This requires Opensearch to be running (typically started from docker-compose.yml) +``` +docker-compose up -d +docker stop shuffle-backend +docker stop shuffle-frontend +docker stop shuffle-orborus +``` + +## Caching +- To handle caching, it by default runs it in memory of the application itself. If you want to offload this, it can be done using the SHUFFLE_MEMCACHED environment variable, connecting to a memcached instance. +``` +docker run --name shuffle-cache -p 11211:11211 -d memcached -m 1024 +``` diff --git a/backend/go-app/main.go b/backend/go-app/main.go index b1304b4f..26644fb8 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1064,7 +1064,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { } org, err := shuffle.GetOrg(ctx, item) - _ = err if len(org.Id) > 0 { userOrgs = append(userOrgs, shuffle.OrgMini{ Id: org.Id, @@ -1073,7 +1072,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { Image: org.Image, }) } else { - log.Printf("[WARNING] Failed to get org %s for user %s", item, userInfo.Username) + log.Printf("[WARNING] Failed to get org %s (%s) for user %s. Error: %#v", org.Name, item, userInfo.Username, err) } } diff --git a/frontend/package.json b/frontend/package.json index 157ceefc..d463173b 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,30 +4,25 @@ "version": "1.3.0", "private": true, "dependencies": { - "@codemirror/commands": "^6.2.2", + "@codemirror/commands": "^6.2.4", "@emotion/is-prop-valid": "^1.1.1", - "@emotion/react": "^11.7.0", - "@emotion/styled": "^11.6.0", + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", "@lezer/highlight": "^1.1.3", - "@metamask/detect-provider": "^1.2.0", "@mui/icons-material": "^5.14.0", - "@mui/material": "^5.2.3", + "@mui/material": "^5.14.0", "@mui/styles": "^5.14.0", "@mui/x-data-grid": "^5.17.11", - "@uiw/codemirror-themes": "^4.19.9", - "@uiw/react-codemirror": "^3.2.1", + "@uiw/codemirror-themes": "^4.21.7", + "@uiw/react-codemirror": "^4.21.7", "@use-it/interval": "^1.0.0", "algoliasearch": "^4.13.1", + "calculate-size": "^1.1.1", "class-transformer": "^0.4.0", "create-react-app": "^5.0.1", "cytoscape": "^3.15.1", - "cytoscape-clipboard": "^2.2.1", - "cytoscape-cxtmenu": "^3.1.1", "cytoscape-edgehandles": "^3.6.0", - "cytoscape-grid-guide": "~2.3.3", "cytoscape-node-html-label": "^1.1.5", - "cytoscape-panzoom": "^2.5.2", - "cytoscape-undo-redo": "^1.3.2", "d3": "^7.1.1", "dotenv": "^6.1.0", "downshift": "^3.3.5", @@ -51,43 +46,41 @@ "mui-nested-menu": "^3.2.1", "process": "^0.11.10", "react": "^18.2.0", - "react-alert": "^5.5.0", + "react-alert": "^7.0.3", "react-alert-template-basic": "^1.0.0", "react-alice-carousel": "^2.6.4", "react-avatar-editor": "^11.1.0", "react-beforeunload": "^2.2.1", "react-chartjs-2": "^2.11.1", "react-cookie": "^4.0.1", - "react-cytoscapejs": "^1.2.0", - "react-device-detect": "^1.9.10", + "react-cytoscapejs": "^2.0.0", + "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", "react-draggable": "^3.3.2", "react-driftjs": "^1.2.2", - "react-dropzone": "^10.1.10", + "react-dropzone": "^14.2.3", "react-ga4": "^2.0.0", "react-iframe": "^1.8.0", "react-instantsearch-dom": "^6.28.0", "react-json-pretty": "^2.2.0", - "react-json-view": "^1.19.1", + "react-json-view": "^1.21.3", "react-markdown": "^8.0.7", "react-markdown-github": "^3.3.1", "react-powerhooks": "0.0.7", - "react-router": "6.2.1", - "react-router-dom": "6.2.1", + "react-router": "^6.14.1", + "react-router-dom": "^6.14.1", "react-scripts": "^5.0.1", - "react-shepherd": "^3.3.6", - "reactstrap": "^7.1.0", - "reaviz": "^12.1.0", + "reaviz": "^14.9.4", "search-insights": "^2.2.1", "shellwords": "^0.1.1", "simplebar": "^4.2.3", "styled-components": "^4.4.0", "yaml": "^1.7.2", "yamljs": "^0.3.0", - "zone.js": "~0.11.4" + "zone.js": "^0.13.1" }, "scripts": { - "start": "HTTPS=false&&PORT=3000 react-scripts --openssl-legacy-provider start", + "start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index fcbb006c..abeb8ab3 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -30,6 +30,7 @@ import SettingsPage from "./views/SettingsPage"; import KeepAlive from "./views/KeepAlive.jsx"; import { ThemeProvider } from "@mui/material/styles"; +import CssBaseline from '@mui/material/CssBaseline'; import UpdateAuthentication from "./views/UpdateAuthentication.jsx"; import FrameworkWrapper from "./views/FrameworkWrapper.jsx"; @@ -38,7 +39,6 @@ import AlertTemplate from "./components/AlertTemplate"; import { useAlert, positions, Provider } from "react-alert"; import { isMobile } from "react-device-detect"; -import detectEthereumProvider from "@metamask/detect-provider"; import Drift from "react-driftjs"; // Production - backend proxy forwarding in nginx @@ -143,120 +143,6 @@ const App = (message, props) => { } // Handling Ethereum update - {/* - detectEthereumProvider().then((provider) => { - if ( - provider && - userInfo.eth_info !== undefined && - userInfo.eth_info !== null - ) { - if ( - userInfo.eth_info.account !== undefined && - userInfo.eth_info.account !== null && - userInfo.eth_info.account.length === 0 - ) { - userInfo.eth_info = {}; - var method = "eth_requestAccounts"; - var params = []; - provider - .request({ - method: method, - params, - }) - .then((result) => { - if ( - result !== undefined && - result !== null && - result.length > 0 - ) { - userInfo.eth_info.account = result[0]; - - // Getting and setting balance for the current user - method = "eth_getBalance"; - params = [userInfo.eth_info.account, "latest"]; - provider - .request({ - method: method, - params, - }) - .then((result) => { - if ( - result !== undefined && - result !== null && - result.length > 0 - ) { - userInfo.parsed_balance = - result / 1000000000000000000; - } else { - alert.error("Couldn't find balance: ", result); - } - // The result varies by RPC method. - // For example, this method will return a transaction hash hexadecimal string on success. - }) - .catch((error) => { - // If the request fails, the Promise will reject with an error. - alert.error( - "Failed getting info from ethereum API: " + error - ); - }); - } else { - alert.error("Couldn't find any user: ", result); - } - }) - .catch((error) => { - // If the request fails, the Promise will reject with an error. - alert.error( - "Failed getting info from ethereum API: " + error - ); - }); - } - - // Register hooks here - provider.on("message", (event) => { - alert.info("Message from MetaMask: ", event); - }); - - provider.on("chainChanged", (chainId) => { - console.log("Changed chain to: ", chainId); - - method = "eth_getBalance"; - params = [userInfo.eth_info.account, "latest"]; - provider - .request({ - method: method, - params, - }) - .then((result) => { - console.log("Got result: ", result); - if (result !== undefined && result !== null) { - userInfo.eth_info.balance = result; - userInfo.eth_info.parsed_balance = - result / 1000000000000000000; - console.log("INFO: ", userInfo); - setUserData(userInfo); - } else { - alert.error("Couldn't find balance: ", result); - } - }) - .catch((error) => { - // If the request fails, the Promise will reject with an error. - alert.error( - "Failed getting info from ethereum API: " + error - ); - }); - }); - } - }); - - if ( - userInfo.eth_info !== undefined && - userInfo.eth_info.balance !== undefined - ) { - //console.log(userInfo.eth_info.balance) - userInfo.eth_info.parsed_balance = - userInfo.eth_info.balance / 1000000000000000000; - } - */} //console.log("USER: ", userInfo) setUserData(userInfo); @@ -529,8 +415,8 @@ const App = (message, props) => { /> } /> - } /> - } /> + } /> + } /> { /> } /> - - } + + } + /> { return ( + diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index f54b73e5..7e9701ed 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -14,7 +14,7 @@ import { List, ListItem, ListItemText, - Fade, + Collapse, } from "@mui/material"; import { FavoriteBorder as FavoriteBorderIcon, diff --git a/frontend/src/components/Dropzone.jsx b/frontend/src/components/Dropzone.jsx index 911bd87b..35b5eb01 100755 --- a/frontend/src/components/Dropzone.jsx +++ b/frontend/src/components/Dropzone.jsx @@ -56,7 +56,11 @@ const Dropzone = ({ children, style, onDrop }) => { }; useEffect(() => { - if (!dropzoneRef.current) return; + if (dropzoneRef === null || dropzoneRef === undefined || dropzoneRef.current === null || dropzoneRef.current === undefined) { + return + } + + // Check if event listene exists for dropzoneRef.current dropzoneRef.current.addEventListener("dragover", handleDragOver); dropzoneRef.current.addEventListener("dragenter", handleDragEnter); @@ -64,6 +68,10 @@ const Dropzone = ({ children, style, onDrop }) => { dropzoneRef.current.addEventListener("drop", handleDrop); return () => { + if (dropzoneRef.current === null || dropzoneRef.current === undefined) { + return + } + dropzoneRef.current.removeEventListener("dragover", handleDragOver); dropzoneRef.current.removeEventListener("dragenter", handleDragEnter); dropzoneRef.current.removeEventListener("dragleave", handleDragLeave); diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 49115072..f314e151 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -688,7 +688,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho {/* */} - Workflows + Workflows
@@ -698,7 +698,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho {/* */} - Apps + Apps
@@ -715,7 +715,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho {/* */} - Docs + Docs
diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 69b3a699..a5665c61 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -36,7 +36,7 @@ import { Breadcrumbs, CircularProgress, Switch, - Fade, + Collapse, } from "@mui/material"; import { LockOpen as LockOpenIcon, diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 354ec93b..b6dba11f 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -1,7 +1,6 @@ import React, { useEffect } from "react"; import theme from '../theme.jsx'; import { makeStyles } from "@mui/styles"; -import { useTheme } from "@material-ui/core/styles"; import Tooltip from "@material-ui/core/Tooltip"; import Grid from "@material-ui/core/Grid"; diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 45ad7dbd..254674b3 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -43,7 +43,7 @@ import { Breadcrumbs, CircularProgress, Switch, - Fade, + Collapse, Autocomplete } from "@mui/material"; diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 9c5e6674..1b3c9d68 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -206,11 +206,12 @@ const SearchField = props => { const avatar = baseImage var parsedUrl = isCloud ? `/workflows/${hit.objectID}` : `https://shuffler.io/workflows/${hit.objectID}` + parsedUrl += `?queryID=${hit.__queryID}` // return ( - { + { //console.log("CLICK") setSearchOpen(true) @@ -402,7 +403,7 @@ const SearchField = props => { parsedUrl += `?queryID=${hit.__queryID}` return ( - { + { console.log("CLICK") setSearchOpen(true) @@ -558,7 +559,8 @@ const SearchField = props => { if (parsedUrl.includes("/apps/")) { const extraHash = hit.url_hash === undefined ? "" : `#${hit.url_hash}` - parsedUrl = `/apps/${hit.filename}?tab=docs&queryID=${hit.__queryID}${extraHash}` + parsedUrl = `/apps/${hit.filename}` + parsedUrl += `?tab=docs&queryID=${hit.__queryID}${extraHash}` } return ( diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 56e9b3c6..0a0e48fc 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -46,10 +46,10 @@ import ReactJson from "react-json-view"; import PaperComponent from "../components/PaperComponent.jsx"; import CodeMirror from '@uiw/react-codemirror'; -import 'codemirror/keymap/sublime'; -import 'codemirror/addon/selection/mark-selection.js' -import 'codemirror/theme/gruvbox-dark.css'; -import 'codemirror/theme/duotone-light.css'; +//import 'codemirror/keymap/sublime'; +//import 'codemirror/addon/selection/mark-selection.js' +//import 'codemirror/theme/gruvbox-dark.css'; +//import 'codemirror/theme/duotone-light.css'; import {indentWithTab} from "@codemirror/commands" import { padding, textAlign } from '@mui/system'; import data from '../frameworkStyle.jsx'; diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index b8966a74..57aa6ec3 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -16,7 +16,7 @@ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import theme from '../theme.jsx'; import { Button, - Fade, + Collapse, IconButton, FormGroup, FormControl, @@ -506,7 +506,7 @@ const WelcomeForm = (props) => { switch (step) { case 0: return ( - + {/*isCloud ? null : @@ -587,11 +587,11 @@ const WelcomeForm = (props) => { - + ) case 1: return ( - +
Apps for each category are shown based on your activity and can be changed by clicking their icon. We will help you connect them later. @@ -684,11 +684,11 @@ const WelcomeForm = (props) => { */}
-
+ ) case 2: return ( - +
These are some of our Workflow templates, used to start new Workflows. Use the right and left buttons to find new Usecases, and click the orange button to build it. @@ -755,7 +755,7 @@ const WelcomeForm = (props) => {
- + ) default: return "unknown step" diff --git a/frontend/src/defaultCytoscapeStyle.js b/frontend/src/defaultCytoscapeStyle.js deleted file mode 100755 index 35c4b7f8..00000000 --- a/frontend/src/defaultCytoscapeStyle.js +++ /dev/null @@ -1,432 +0,0 @@ -const data = [ - { - selector: "node", - css: { - label: "data(label)", - "text-valign": "center", - "font-family": - "Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif", - "font-weight": "lighter", - "margin-right": "10px", - "font-size": "18px", - width: "80px", - height: "80px", - color: "white", - padding: "10px", - margin: "5px", - "border-width": "1px", - "text-margin-x": "10px", - "z-index": 5001, - }, - }, - { - selector: "edge", - css: { - "target-arrow-shape": "triangle", - "target-arrow-color": "grey", - "curve-style": "unbundled-bezier", - label: "data(label)", - "text-margin-y": "-15px", - width: "5px", - color: "white", - "line-fill": "linear-gradient", - "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["grey", "grey"], - "z-index": 5001, - }, - }, - { - selector: `node[type="ACTION"]`, - css: { - shape: "roundrectangle", - "background-color": "#213243", - "border-color": "#81c784", - "background-width": "100%", - "background-height": "100%", - "border-radius": "5px", - "z-index": 5001, - }, - }, - { - selector: `node[type="COMMENT"]`, - css: { - shape: "roundrectangle", - color: "data(color)", - width: "data(width)", - height: "data(height)", - padding: "0px", - margin: "0px", - "background-color": "data(backgroundcolor)", - "background-image": "data(backgroundimage)", - "border-color": "#ffffff", - "text-margin-x": "0px", - "z-index": 4999, - "border-radius": "5px", - "background-opacity": "0.5", - "text-wrap": "wrap", - }, - }, - { - selector: `node[app_name="Shuffle Tools"]`, - css: { - width: "30px", - height: "30px", - "z-index": 5000, - "font-size": "0px", - "background-width": "75%", - "background-height": "75%", - "background-color": "data(iconBackground)", - "background-fill": "data(fillstyle)", - "background-gradient-direction": "to-right", - "background-gradient-stop-colors": "data(fillGradient)", - }, - }, - { - selector: `node[app_name="Testing"]`, - css: { - width: "30px", - height: "30px", - "z-index": 5000, - "font-size": "0px", - }, - }, - { - selector: `node[?small_image]`, - css: { - "background-image": "data(small_image)", - "text-halign": "right", - }, - }, - { - selector: `node[?large_image]`, - css: { - "background-image": "data(large_image)", - "text-halign": "right", - }, - }, - { - selector: `node[type="CONDITION"]`, - css: { - shape: "diamond", - "border-color": "##FFEB3B", - padding: "30px", - }, - }, - { - selector: `node[type="eventAction"]`, - css: { - "background-color": "#edbd21", - }, - }, - { - selector: `node[type="TRIGGER"]`, - css: { - shape: "octagon", - "border-radius": "5px", - "border-color": "orange", - "background-color": "#213243", - "background-width": "100%", - "background-height": "100%", - }, - }, - { - selector: `node[status="running"]`, - css: { - "border-color": "#81c784", - }, - }, - { - selector: `node[status="stopped"]`, - css: { - "border-color": "orange", - }, - }, - { - selector: 'node[type="mq"]', - css: { - "background-color": "#edbd21", - }, - }, - { - selector: "node[?isButton]", - css: { - shape: "ellipse", - width: "15px", - height: "15px", - "z-index": "5002", - "font-size": "0px", - border: "1px solid rgba(255,255,255,0.9)", - "background-image": "data(icon)", - "background-color": "data(iconBackground)", - }, - }, - { - selector: "node[?isSuggestion]", - css: { - shape: "ellipse", - width: "50px", - height: "50px", - "z-index": "5002", - "font-size": "0px", - border: "1px solid rgba(255,255,255,0.9)", - "background-image": "data(large_image)", - "background-color": "data(iconBackground)", - label: "data(label)", - }, - }, - { - selector: "node[?canConnect]", - css: { - "border-color": "#f86a3e", - "border-width": "10px", - "z-index": "5002", - "background-color": "#f86a3e", - }, - }, - { - selector: "node[?isDescriptor]", - css: { - shape: "ellipse", - "border-color": "#80deea", - width: "5px", - height: "5px", - "z-index": "5002", - "font-size": "10px", - "text-valign": "center", - "text-halign": "center", - border: "1px solid black", - "margin-right": "0px", - "text-margin-x": "0px", - "background-color": "data(imageColor)", - "background-image": "data(image)", - }, - }, - { - selector: "node[?isStartNode]", - css: { - shape: "ellipse", - "border-color": "#80deea", - width: "80px", - height: "80px", - "font-size": "18px", - "background-width": "100%", - "background-height": "100%", - }, - }, - { - selector: "node[!is_valid]", - css: { - "border-color": "red", - "border-width": "10px", - }, - }, - { - selector: ":selected", - css: { - "background-color": "#77b0d0", - "border-color": "#77b0d0", - "border-width": "20px", - }, - }, - { - selector: ".skipped-highlight", - css: { - "background-color": "grey", - "border-color": "grey", - "border-width": "8px", - "transition-property": "background-color", - "transition-duration": "0.5s", - }, - }, - { - selector: ".success-highlight", - css: { - "background-color": "#41dcab", - "border-color": "#41dcab", - "border-width": "5px", - "transition-property": "background-color", - "transition-duration": "0.5s", - }, - }, - { - selector: ".hover-highlight", - css: { - "background-color": "#5f9265", - "border-color": "#5f9265", - "border-width": "5px", - "transition-property": "background-color", - "transition-duration": "0.5s", - }, - }, - { - selector: ".failure-highlight", - css: { - "background-color": "#8e3530", - "border-color": "#8e3530", - "border-width": "5px", - "transition-property": "background-color", - "transition-duration": "0.5s", - }, - }, - { - selector: ".not-executing-highlight", - css: { - "background-color": "grey", - "border-color": "grey", - "border-width": "5px", - "transition-property": "#ffef47", - "transition-duration": "0.25s", - }, - }, - { - selector: ".executing-highlight", - css: { - "background-color": "#ffef47", - "border-color": "#ffef47", - "border-width": "8px", - "transition-property": "border-width", - "transition-duration": "0.25s", - }, - }, - { - selector: ".awaiting-data-highlight", - css: { - "background-color": "#f4ad42", - "border-color": "#f4ad42", - "border-width": "5px", - "transition-property": "border-color", - "transition-duration": "0.5s", - }, - }, - { - selector: ".shuffle-hover-highlight", - css: { - "background-color": "#f85a3e", - "border-color": "#f85a3e", - "border-width": "12px", - "transition-property": "border-width", - "transition-duration": "0.25s", - label: "data(label)", - "font-size": "18px", - color: "white", - }, - }, - { - selector: "$node > node", - css: { - "padding-top": "10px", - "padding-left": "10px", - "padding-bottom": "10px", - "padding-right": "10px", - }, - }, - { - selector: "edge.executing-highlight", - css: { - width: "5px", - "target-arrow-color": "#ffef47", - "line-color": "#ffef47", - "transition-property": "line-color, width", - "transition-duration": "0.25s", - }, - }, - { - selector: `edge[?decorator]`, - css: { - width: "1px", - "line-style": "dashed", - "line-fill": "linear-gradient", - "target-arrow-color": "#f34079", - "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["#f86a3e", "#f34079"], - }, - }, - { - selector: "edge.success-highlight", - css: { - width: "5px", - "target-arrow-color": "#41dcab", - "line-color": "#41dcab", - "transition-property": "line-color, width", - "transition-duration": "0.5s", - "line-fill": "linear-gradient", - "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["#41dcab", "#41dcab"], - }, - }, - { - selector: ".eh-handle", - style: { - "background-color": "#337ab7", - width: "1px", - height: "1px", - shape: "circle", - "border-width": "1px", - "border-color": "black", - }, - }, - { - selector: ".eh-source", - style: { - "border-width": "3", - "border-color": "#337ab7", - }, - }, - { - selector: ".eh-target", - style: { - "border-width": "3", - "border-color": "#337ab7", - }, - }, - { - selector: ".eh-preview, .eh-ghost-edge", - style: { - "background-color": "#337ab7", - "line-color": "#337ab7", - "target-arrow-color": "#337ab7", - "source-arrow-color": "#337ab7", - }, - }, - { - selector: "edge:selected", - css: { - "target-arrow-color": "#f85a3e", - }, - }, - { - selector: `edge[?source_workflow]`, - css: { - "background-opacity": "1", - "font-size": "0px", - }, - }, - { - selector: `node[?source_workflow]`, - css: { - "background-opacity": "0", - "font-size": "0px", - }, - }, - { - selector: "node:selected", - css: { - "border-color": "#f86a3e", - "border-width": "7px", - }, - }, -]; - -//{ -// selector: 'edge[?hasErrors]', -// css: { -// 'target-arrow-color': '#991818', -// 'line-color': '#991818', -// 'line-style': 'dashed', -// "line-fill": "linear-gradient", -// "line-gradient-stop-positions": ["0.0", "100"], -// "line-gradient-stop-colors": ["#991818", "#991818"], -// }, -//}, - -export default data; diff --git a/frontend/src/theme.js b/frontend/src/theme.js deleted file mode 100755 index 051cc846..00000000 --- a/frontend/src/theme.js +++ /dev/null @@ -1,102 +0,0 @@ -import React from "react"; -import { createTheme } from "@mui/material/styles"; - -const theme = createTheme({ - palette: { - primary: { - main: "#f85a3e", - }, - secondary: { - main: "#e8eaf6", - }, - text: { - secondary: "rgba(255,255,255,0.7)", - }, - type: "dark", - surfaceColor: "#27292d", - inputColor: "#383B40", - platformColor: "#1F2023", - borderRadius: 5, - defaultBorder: "1px solid rgba(255,255,255,0.3)", - jsonTheme: "brewer", - reactJsonStyle: { - borderRadius: 5, - border: "1px solid rgba(255,255,255,0.7)", - padding: 5, - }, - textFieldStyle: { - backgroundColor: "#383B40", - borderRadius: 5, - }, - innerTextfieldStyle: { - color: "white", - minHeight: 50, - marginLeft: "5px", - maxWidth: "95%", - fontSize: "1em", - borderRadius: 5, - }, - tooltip: { - backgroundColor: "white", - color: "rgba(0, 0, 0, 0.87)", - boxShadow: 1, - fontSize: 11, - }, - defaultImage: - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAK4AAACuCAYAAACvDDbuAAAgAElEQVR4Xu19e9CvV1Xe3r/vnJOcBEhBSgMEBaoUK9POCOVmAuP0HwcUCNYZSUsh9xv3hGl1qNippQRE20IFEhQoBJiaKVpEgQRnhD+0QHSmRkAsxE4doBZQTs71u/zezruv6/Ksvffvkn/qd8bBfN/3XvZe+1nPevbaa+/XuxX/Tbe6C/ece8qOd5dNk3um9+7Jk/OPds49zE3uyPy4ST5zCr/y4f+sfxO4j16vHsqfmV7gpgm8Yzm/lP9+km1B9+W2zn/zzk2sDeR55ffy3enn1Lf5J/1e3bb42kX43/Do1nt9fUdpLrSbbFt8vvls+idlm/qsaJPWuNK/TfvOLU44577pnPuSc9PvT95/9szu3p9c/IH/c2oVKDbeyB8z/Yz7noNd9zzn3U+5hX+6m9wjnXM7GqXCHpbR0+PnjudGxEtBkzRo02vFtRB83rkAXPqPGD4MnmWGGa3CDqp9+pp4Bwd2dCwPzIXur6D1xaGRXaQz8nf4Kfix1/3joDXtDm0jHVbYspirbZdkj4Npcv/XO/8F59xdfv/o7zz0A1/9yxEAd4E7/by74OCke5Fb+Bvd5J7unDvGHmyCKiMzX40ByW+XQCxvyoSafgGMBT0fgTayXmA/kykr8PI1kS0JIOMPRiSJ7ctOWfuI+mcw4Uj7lO25Y0TQoyHGbKsjJbqXR5HoKijK6nuz3bPhokEX+f4956Z7nXP/8cz+w//bxR/4H00GbgJ3ep170sHC/WxgWecvUJ5AemrLA4NF2cCAa4BDSCbjYVR2RYN2ZXlAOgxZqcXUDLh9toyvSkybopAJ+KBbOpHADOEjoO1EkVYU6LB0HbPUDtaN8N7TzrmPOnfwry9631/8mcW+JnD3Xu+eu3DubZNzT1Xhu8eyxaXw47lWBIzcAW2fGSpoM1WPgzYNWqSRwqjsnSYokgMyvW5JnzWZNrQpC9tC+ZVZW1Ek9GbBQK8ZuS99mrrW1sPh7XXsTeDmqHrvYvKve+j77/8MAi9E1v6t7vnOu7c7555gaM4SuiHGOsajcV8xmeEUMSDFf4yJQLiUmpYNTg906iVCl7b6ltu4gi5lTMs6Z4RpwLQsVMP+IZaNTtYngRoNI1uOzwdYhJxtEhqKAFsdvoI0XHy/m3ZeedH7v/ZxCV7Vir1b3HP8wr3PBC1HDsog5IkJxSdDHAyBlRqBgyHwAANuI3sg3j4G+shSHASQaRlbZtDW8AmiT7b3TLJD8qACrWrwkclYg2l70qACWow5IZvQ9lHQUmeZ7p+8v/Lh7/3z36NDw6w7a9rljvvw5NwPWykOOuEono5YT8IvXQNBK5yhGnwFVoCgZYPYZgvSBtnGMMkJnR2ZrIh3Fga3mGoR9YgpL2xAjbGlBC2YTEGWJu01J3mkrwYGuFP25EF1XMFj9y6Wiyse9p+/9hXl2il78G7n3T9radomMVphpMdGUG9wAJRLYJ4Wz2CZ73QmUpkxa9jlA2fmKvNz0414Fm+Abw6b+U8D4Km+xfsb0l5E99Z+S4mApIHhaEz2bCAPSmMkaNMzW4Av94ac3vtPHdu96TG3f32evFXhuH+ru8J5f4dzrmYPVGiKN7Bfsx/syVj+C07AA3WQmlYBa4TR+bpWnhYCgjyLtN+OBrbezPdAwNcQCjq4EAsaCEAS8BF4PNIZoBKhWdo9PMO0DX5vbmGTRMi41SgyxrQlTcmsVQB+2jt/3UXv/9qdxVLTre5RBwv/m25yz+Qspe2NQWt5ZEQ5x79kMgzaoTC4DM9SuoqDvcEWSqKsNlnJHYvvQ5oWvRutiInrijdwALGBLZ003sEcMl7DSEdHoGTHyoQ4eqTxaqymzdmYuq40wrS1D5A80i+985/1u0de/LAPf+Vb4Y7917uXO+dud84fRcClIOKdVwPGQZRuNBuj6DujMHakCUCgadUsubeMS1A/vhRLBxZEgR6g8juLbUzAE1tWe8wslv9hh9HyQPknZNpEAsUQaJWPDgpttyCjsDxuTMQMwAOiUoTknNv1frrmovfd/wE/1x4cTP4jbuF+3AItwFe81ACGXm0SAC/5UTYGoSBAdwCwmZE9GAqhInmvBr8JPMmAo0zLB7G5YmdMALkTy/fOz5+NonO0VaJl0Bmyh3jDakzL2xL6JnLF8dGjk8xKDBWPtc3euY+dWT7kJX73VveMhfe/4Zy7ONOcgDpnP+a+iC1WLpZJ7RPhArLCPD7snZSVpnmGkgMZTdIrA5BIwOoHBkDLHWs1eRBMZ/Wr2FWGTcFmGQSMZYhjAInAB1C2WQIlrBGUi1JtUaJ8DHrtVIJtwyjZ6bxKjA3Q1n590y8Wl/uDW9wt08LfFgpmCCgx81FroU40KqhUvNLPYu+0NBSYiBUckjDHxtX0eCJJeqBN7cfh2WazKa/FK4MK+wGmHV/t40wbmyoBj8erYDQ5VGYC1VzFmBZpjUzEjGIjhREO5NS2A+f9v/T7t7q7nPc/qZPbYuhZeJdGL6lIMgFQ12gsJUBxwObLxP1KHujO+8nQZT15ANiOsrRcooZVXjDEyyovAzypbDKNQRifdSZiFXTI9m2JQCNBZcAcv+x7w9iF/zEACwlLZEd6ZZiFmWI7vJvu8gevd/dNk/+hPFDa02rwtXOZdZij8axlUoldwnilAYaRDKa1c5ixizxMVqfAEcUIo122RW2OCwv8/aPSotolLn5Y+nBkRWxk4aQuqGu7GOMxE5nPtQcN0DLggvGGYySjRR3L1L77/P6t/tvOuUc0gdvSZb0qL+hxtbSw/tkGma49SB3J2sl4R0SOBqNm+AYbFW9Hz0KgqIPIgWsCgHhzvaZ9rwYsjgKyzZiJMVmNVoiNsi3oWxi3BlkUwshMW7JO35mBu+dc3LmgwNvUfZVNasfH5AHVX/kOvHNBDzbXfX3jZkxnThliwSwtSswcLQDng7haFVUEWb99umAm6+5iyzA0yDbcntDmhQSwo2U5EvPzI3pWOzzDC/OaLtMm0Pm9GbhVpkD0hheX9hZwpxdWPYQ6iiqMweCgUMizB7nBYtXOeKeo0optXC1Mg8WFagMzAtV62mpKwCiC4miBdcCNSRg2aOnQNSu4Amn5MAi2LGyzoF2aaDtLflfCS2kDtBPRtBVf+cr4Dr+XgMs7Tn9CA86uNkDBr4k/AY0jQ0XRsoIdUqV9NXjLUXrywAJ8Yj2LBYrXovsrqMwI1Mhlhn6xUWq/I9tzTKsT1hNO15dNkqV5VFHAE7bTzpGep/6Afi8jXXWMBnBB2CeGXVUeaNAaYV5kD3IYLP5mFpTwlFSkx9VraWv1m+UYNqB46AL2Q6yf2lgr0HRoDX1PBTk1EliEgkO8dPhcLs+Ba9ybTRsuXmVFLI6Cwgoihh7gBcFx4JohKoli8nAJqGhczbIZQIWsynXCSA2mHQ6Don0YuHYY5HvEWsArMKs6by2Wthml9pkzeWwVilwI8KQPrdJEU/rI+xtsa/SfQwKQ1RRlKL1OLwpph6rAtUEbH5z+Xkm3LyFoOMtXwwkLBO1g8XgeSADa7COlzWqASHhKS0RU0rAwqJwyLrOGCUrD4XFKrtqusDRMefEVsfyaVR0ya17AK7GLrWXmsoxGioOKYXB4p0SjCA7ZyqpFNh3KTRG4LbZIf4vMKXa66hYKyh2ZJfNl3Poe6hhW9iCxDGkjBF6nAJwHi5EQT1hwrrBYYatOjkDM5I3FCxrJxmt9OaDs2oP2ZCqMd2jo6osL7SiJbcww1okCfu+WzNK5mXUjXjZafmAc4Lb2ZSzF1IOlafnzqIcaOw9qc0jnKhD6LMByu8Tbx3KhYiAt0ALDZ9uRqUJiAhm9UPagioTS15ZDkrqIEu0U0WBNW5+fbwDABflXjhMiXSxihDZK76x/I4+t7U3ARR1AtbRCR5mxR4TC1myarYhx7RaWcNkoU8t3KrXsMGOsqInoEJqCnA2F7x5Lz88m9m9FOMpu5LqxsksepfBuaquuAsiXYG5QT6vG3SCf1nVCMpSgGTqKHYqN/t4tCxN+sAjcvjopDvnSRiJcLOOW9fkm6LAnjw0sdryxe9Hyqnhetmyr5hSDNqGarLqxsxWkYxhEkydtyX6mz7P2aaerTVwNtENRT/Rfw6mzcJJu8BC4Bf55JEjnGsDV9bRGI1TdQZUgVVdZUUCyeSsKGJ7bOvfALD6viwsVEONOysOvBTxZTxtTekWDC5aSsixcR5weArcxEVt1qw1lxnVAW/ycSRhgU3Xhwmng1haE7BCuV2XSO/0AKn4QyMWKWKQaOYlrhArmVEL3mQOrwR0v7YX4PBlNxwT1nm+ExiwS2gfEVdCOti0afgTYluyR0my9ZVwbtNl17D1szPk6EsSF1FmYg00KuP0TCTVo1SY3GOo7RyL1QFFop2afMmzDrTgEF6eqbGSE93CBdphcT8tlUx/wehuR9V5UBL7i3rfEtPhQPQu08ffBqQoZrFZ7wHGWbGKNAxnfdeUB1dsMuCZoFTC0hGiHQnz4XGaMuN1jLOVVo8YIeMigRT/lk6Tig3bZIHcKW3pwdxZyxnTK0doDBHoJFM6erD1W9kFJwnFNC8HHcMLbB6NIa9LO5EP+obavAheE4HIvCvkpTKk/WUaqupY4ee6cAkSKrqjzOuzTkIkA1HRIw3iFaUHRi9K4lkRoRhEN2jjuKzpkM7cenpWVChnOFKuK1h/L08J0ngU+I11GMWUeu8pAi5eY/d7rYlahT/scEklbsf1JJNSmx3lv1dLyk1usMBqZsFpesEoFhhqcov1aEgJKmrktqQi8KT9Im0UYDMAeBC1tuLFVR/SNs/mcMsQBsV/WCEoTK2GAvvdDPHI6MfGuIVMcR4XvtRY/AHDJAyDTijBYrCZZU8oDYGATOCTE03BOHawJjDpoOERZuUyUORgJ0/Wa0qyGU2ikVU1rH/ckB9bapmT1TUeq8Xpa0L9evW/zFHZDcil5YGtuv5sYV64mSX6tPyPWMxqS5AFlzAIkU37USZJkWsYsEBi54ECwUiy1EA2XbRY7F0zH6NScNlkayYPVJ2J27QGaIOWRk7JrTB5Qs9UdvzpLwPCSbIBroBtEwAa4OVGc/O7rdjiENKAKfnRDRhcXJNs2JmJQG5FoaTKZZFlgIBOMaHFhtYlYn2lTaWIa4dijUdDSvnhwdBMBJyCEUqheAIWWcG25xh85tkAQMSijd4dpw4uMsknO8EPAZTWVZWJiMd78brAiVjyydboMaZxmaTLi1pLgWgcqI00LDGy2O4GvKV309nG9WGNXaWVp0ZY9DeCx72yMgraSTZ2M2hKOTpJUerSXPSi2a0QBIeQr47ZCt4yy/cxBsnUFQJywII/LwxK3k0BJkn8PU2ZEFrTAAx2NL7HGd1ttxEzDmRbd3znzoHS4x2RygYeYKnh5I3wXpkWM1ulXfk2LqBioQI2xAJ2yMwRudhfcvgHgjoR5fGJiZs32ipHWZYpZWvKAGAUyknnvINOG53Mwjy24EJAQh1w53RWEZXw/Zz7iJIp0eIiOfwY5WgbK/AOYRCvGtLT06KZSKSEa7TP6FoELdVE1TDUYQH/v8LkVWXAcfJxpYxtRiDdYkBJWKWiR90MtHs6ibUsmDdqsaRX5dEoTY8pq6Ms2iSdQBDJAKyJclWc0PYpAZrM0cy7xvQoWTcPybe78yMJHHcfQzt3XsslZ+F02Mg+DABQDp4CbeUbx0T71ThF+CM7Sfw5MxkxtJSdj4xOxvjQIoC0YaO5h6xS4w2382C4MtDzSjTOt5i+bWa0l9vAMk6ykEwwtMUOHlMCtoMjvR40IBwNyeTAM9hz00nOLkUswTE1oMnWs07WPn7dzmSx3GZkWnOaNBmx04+Wgpm2A1pJWbZbX8sDnhRTm9YwtCyj4OCAZojUnjY7ZYtGksI65MmwZ2xHg4igqgIvSMyDU9j4SEhpmTsRYgTaUBsVrbSas7IA6hu5DedoRphWar+VQWUsSpxypQNM7MizbNRiQrO02t9soWl1B0zJNrO8L6b1YeCIC5IimTZgR8oU+iLI5AS7RRqWBRgPsExPjBKKlbZTIG89lhtCpPl7XCk3cGNUmwtM7oa1ffGQ7yur3jjK7ZjU88dMTn0wWsdWD28eFBFD4r2RFkCvsYjJt2yELHEmUSsAFxmqlnhrbbTqnqLCiCNvQmAnVqYlHLnDuyBG1LV6xeJMlyeF0s4XgZNPKsZKBN5gsGt1c0RMn8zSS7wk4/DWAWFQ7ePRjNRTLAzftnSWk1l8RG4+QCLQj0kBEa8HA+ccM3OAl5Rqkv4ayBw15AItBUOcMlk+TuYrBhTvyYz/jdn7wR51bHojQxH+EYyl1n4oEzUcO/tGwR79BHEyDb5NBWr+mtsf7hdv/8/vcubtuc9PuWTsPLB6igTtQzBNsO764kAu45nSK5Sj+3GuP6P2IaM/U2l+24WyFG2ItAWttVO73O+7Yle91O0990eCwHl4mLbD/p59zp3/5ajedPSl0KdKk4XeC4AQ7lhdIQhqti0DRATu/333tkbkxMWuzxopYjHV9pi29hsciGZMkaml5EsshcDf2xP0vf86d/vfjwGXkC8d9BXkAIxxzmFD2iIOTd4Fxqw6TtkgPEpqWd6ABWlF7AAGuDCAmibV1fIHhELgPAnAx02YJUse9tzwdyAx8/jU1OeQy29utcEqQSJ1zryHAlbqPAHZM2+QHoMUBJLo7TNsqmjkE7vaAe+ZU4hQJ3Phzn2nR2M6/G1uxY4G1FATJd/PMg4/ANSZEDLhi2bFRLRU6S9JW0bmwUbT1a9ledUpw72LWuL92qHE3gG+QCkHjEuDW2S+oBBiYiJX74TIumT8C0jKPstLj78+95iiXERGsheYz0+Zbw8+t0kSRPVCgLS6MnUWfvmIAfmbcqw6BuwFuHQcuZzRddjk+gR5jWj03wsVE5b0J9LGdArh6GZezcaMAPDNqcgOmh1i8seVBdAoiN+SoUIc5BO4mmA33RuBeo7IKxt438T40EWvIA2Ns65gT/crkCcUc07iUcSVwx8O7jiujx4RWT1dgz6ZCM9igcX/N7TztMB22LoI1cKuuZHMn40gpLoDXWFxIDcd12gmwoSGa7Py51xyb9NfHI413GTCSdnw9ERyR0/un05Ql3LywRJ9XRkM/P152JAH3heuO29/4+2bgnvrla5wLeVxRy5HHQuWjENOOTMK0NIi4MWp4W9F3GaQCB67MHsQKLBTes7vUBunMQ3vHadMxSg4GvXsRvhcbGfcQuOt6YAZumJyhCfpaoMVkFtuoQM9rm+E19Z7cnPk3ALiV4eCsnjKh6NhK1f1EBkDAmx6fvPsQuOvitdxXgBvSYRJUePLMJeGaK2I5IjP8kMhagAdIa5mW78696li5fZWUVepA4MVVmTZLi+pBoxVi5NyDxSHjborcANxfmidnBLhoPqGZMr0abbw0AE8Ij0fa/AcqCfkzGL5TzYzPwB2bSeIwMF7lxfXqaoCvVVxx08cipMOOHEqFtfEbgXttzCpkxh2aiM2vHNluwyf3OLKiOYwBXHLSZwKuqMlU2obYhnQMZgGgxybAs9NNbLHOR0IXgIduJeAeaty1cev2v/x5zrhxQPPsgmjStD8s/H297IG9YVYCV8uDQoxkQcyffdV5BKbjB3WsxpYo84Bmp8ZELK3E1SGa17oX7ryrfnWzydlcDjmBU6bXx8L6d9KZB30KgVGB1chbWD4L3LCz4/a/+N/dqf9wQ5QKa+9a6JwHkV4NU16S3Y0a8GAaMUwVuCZT8swAZNnsp3lCxewUwdj8hhjeo8THqbw4gTsw7q9uJBX2P/NBt//Hdzvnd8TIGlkUGG2MyJGf2FhlzMerBozBKDeWzSmvYvATs3H5fL9w04nvuP2v/qFzB7KemYd4u2BmbAkYnoBEGw1wU5JK8NO4ziXgdlbESKfXYloCbLyF3GbajF713i0A99ydP+v2Pv0O5xdHe6dVJjvSyJEmisBJNTMm52WHKKM+06S7dggyixaF3yR8s6+kCPBmgBR2nyOXdNogB8TGxpWLwIk9ObbY5lRAlgWwgWXtiZ4/+6rz4bkKVe3wF+vMA2ScWtKWjISFuRVmyHYaybQZKNsA7kfe4Pbv+RXnFkcLVzFi6hQSxUhisaI16UhDM3Y2rdKbdGDtzxw0xqwbCSTbjh7ZBByts/0+NgVMxKztU+Raf/aV5xtTMdn5uBrGVEFrIrYFeWB++G5+71wddtV7NpIK5wJw35mAK8v3DEZMA58nG2nRT1yMQBsHqSCxOPTIZKfBnErLcuCpw5hDA0bqaRv73xRiBPhauGD3oolYm2kLb2Hgoh2kwjtaTKM+Kd+fiMUBHWDa7DlzrcLVmwN3LwGXj//IwBrn05rhr2r9qi7QbmM0mPF3+S8l6iEQgN/1o4hk2SAXmAhCS/v2xgC52qo0s3g2IQ2oaTURAMaNg1ZDEjr+xy5xK/dZIR7+nuhF677S1awDF9sB7t3vdG5nlgrFl9WuYXnuQf5ZhypsF8p69SMo48Dg77EkSA4F8f9nabaSQ5Ybx9tGEbhZygsxLRkTFuoXTgBXG0WHGkvTJb3aK5hRo03ytOYZXgBUs8a9es4qvEB57+gvzn3oDW7v0xS4IzWnKGwnPrRCaJa1pX8UGIjtKr9C0LJBRPcLSYePouJba8qLRqSLiL6pPbimRWIK4Ccv46LzglURV2wfAS7WtKU/JhOSgbS+hlhoWDaa7Etqnm0LALU14L7LuZ35bAbLIanhJSAIyw0fgTrCZhq4kTmlHTDo9WmSqG9Ivo20zXiWkofSmQ09mw/0s3aRM8+tkTkBV4NWV/J0BrZ4HfZGrYdEOqkl2hGo5rLGq+/YjHE/8ga3d/e7yuRMM7UuYuZ6UYQy9gCuac1zvBh7WkzbYPSq6dipjkUi9E6DDG1GB093okjxWS/OcEPhHWMny5m0uFDmrVpPE8Dmpb2zrzwe7s8J8OLZTLwAMOa/q3pK5Mn8d7HB/GuN8L2g1rd0KkzONgRukAoIuDKKWBMxzD5kBFJN81geFIXakvJCMgR8kagMSwyoA3o9AVd5bT/Er6tpS1daEzHm0DJn7mepcLzkcfGBxRZoeceK90jW0TMY8DmmTigjMqX+544776o73JF/tInG/VdR44Y8bp6Pyn41Io2haQNwm3laBCokQyR4CJuRd8txi5UFDVnB0D1amogJSZlgRtN8fHD4Q4NpG4sLBULhGXihpwAXAg/qWq6rasPHmTY0rCUNwt+VHha3bB243NDJ8NDvOkxWl2/HQaHfg0Ar5Yd1mLU8twAAiICCk+34Mm6RI+UBtM0GaAcWFyo+xLluJAL7s69IUmHVCvho6UQufdCqrSGmR9qGK4M7/8diS8Cd87ghHYZDIyO2sp9JOxVTVi1Q2E6biRpuhwK7B4ydKWPAG8vTctaMmjL+DoIWEh1dc7E+jRvsGRWWsJ0ixvQLf+YVxye9ImaHbl4sI/aWQaaMQ1oOVG4yrWaZ2Bu5fy3uOTvv6tu3IBVmjXuEgSWHXrL4l3CJQZG7FM0vWUKEdzXiI0X0KMqNkIUI18X266W8eqfLNORBdMrBxQW9/y05CsGOP/OKC+qP2GPqoKHywrU0LdI/NijCWEvA+53tAPeeOR12lEoXcEJ5Z+8ca9s4KKJTgu8tIOcmYwPly0j2YF15kDSz/owtGUcdQUsE8Uke6HYjohqzHwCuwbbprYoBqb6BFpWrYsZkpxo+BwMSP9LginMVtsG4u/e8y/mdo8kxrLZhRwunby9RPW/HCQsJH6nzGHXSjwQFYe4MdvV+zszxMrmZjLRt/tMcbZpRsO7EXUEe1JVXrmmzr4L5hJ6ItTIqFbjaY9TDY/+6wCtQDl8gJ4CPf2D3hzkorBBS+5zEe7fCuD/ndu95p/M7x0S9MAkjFpNNS7d4zJPdzt9/bu3T7KOwLh2Twf6f/r47+F/3haL4pKdY/MrprCpFqGSa3M6jv98decpzNDbRGJEFgtAav3DLb3/d7f3R7zq3v8+iKm3EWPUb719AZ1oNaxaoZ2UbXggmYooI63v8mZtnqWCAkcyelUhuMi0qlgGgbzgLazNcgNiGVJiB++6kcQVmgvUbNRkH++7YpVe48696q3MLq5JKPFP8eOZDv+B2P/HuWiuBpJp1ntbBgTv67Be7C69/i3M7qKa2/e75r/tf+oI7+bYbnIM7IKwNBCgSVPwwnGgnVuWusZVGbXMjEvgzN18IAjzXHqsxLZcG+Ulzco+ZEoKWh7bmh/22xrgWcO0JaiCKg3139NIr3PFNgHvnL7jdT94RMiT1H9J98XcsVG8FuJ93J992o3Nye3oam6YmDY0BTDs3NLCttB+aTFbQVvLV/VdFTnOMJsCt+kM1qK17uW8v2qtweQigHqahEL0zh9RlYMnzrn73hlkFi3E7Kbm57csDd/TSl2wZuEqjJq0IjrNaLt2xZ7/YXXD9bRsw7ufdyV+8MTJu2eWbVYsBPBQVEgCqRGg7fdXUekVMbEXRGj3PgCDjqu81IG+RjYvyIDhiU+y3S//aEwWiBefNktfM6bCf6MdE44qzd/6c25ulwlxkk/91qvZL17YOXA3aktaksixT0/JgY+Dufenz7tTMuEQqzJo2t4RzC2VCDcxqlzHQsu/NjdieXBNwVoFbG1YAaIh8rImTrl0RtPFd8pwyoIdpYXM6V+H8DYF77s7EuAW4/bLG8g2xgz139LJZKrxlfY175791u5+4vTqOsJ0KStSpZscJGndzxp13+YZV2qGUGp4PBWnQ+E6IzbJ56UFKBPAeUhdTgFuS7ubhutb+MKJTVi1NHP4ehJvcNH/KkhLjwm0HuDNwZo3ZTmGpAvCDvenopVf441dvCbggBFcCSY5MkbwVxv2CO/WLN6ZjRkcKgYxJPNS0gnxK20HBzFSSOlrrV/FbF9YK47ZOzGvSONq5gNhyYP9aaKBhGPSpoa1JhdtxVoHurxNGDyYJk2tz7OsAAB/vSURBVLOXuO0CN/afARZOYn18/49sxrh7X4rAnaXCqlvIWfYgTci4IpMMamQOGDABdkQFXH6vP3PTQ8J/K+8ugM3/oTUte6dmjPhIAMbxw/FQnWjacBiAO0/ONtW4beDmEKc02ZaAe+4TdzifsgqqOi9EgTpnrsCYgXvgjv7I5RtJhQxcfSCI1NvAoUJjVpMHHKMD2YPUYVjuGYBL8ixjZ4jFnQsFq+ZMEzeueE1TWiQJIq4pTfWzVNgUuG+Mk7NQq1AdlEwyZ7fzcCKxJeDufuIONy129A5qsxY5te5BAy4GLZNpiWFZDUkGsrqwSJDQ8Dj2CBeSbWuUVlp/ttbpxLhwwhXuIIdDhJeinQtISAPaR3uKoDzQK0nqPIfFgwFcvn08GszQfvPkbAtSYWZc7jjJbnq0+KpjAO4sFd68QTrsCyEdNp09neBmT5BUc3A9bVwJJUTIC2YGACtIkL+3gjkBdyTdRQYxOT37Liz1tMZEJ1xmMnRhWRIfa5gqr5j/ujXgzlIhTc5KDUB+U6PgYzlr3J/eWOMOAldvbJyzChsCN0iFtybgijwuIrIwbO0KrwhbMidQErTnkOnvWjZxh/ZnbnooOLbK8gyZ8mrk7EgDV9K0XAixmWSEU92efv6179pQ477R7d1N0lEJryXlxZxRhLitAfc9fOVMTcZQfncG0NIdffbl7sIb1mfcCNybuufjlqE0vufMCEVEqQqD7jaihHFAVEBe+NM3PZT7AAvd6XjJ1Jj2woIV4kAtLayN0BOx5jljs8bdMnCzCIOaVkaRWeNeNmcVbtsgj/smFxk3Lfm2zqbN6IjyLWUVtgxc5TTVc+OhlpKokFPpjY2McMojUZSv72CA52QWoo8ALvKKGi5tabBd0DIBb0mLLQF3lzJuBgViWhHi5lqFY5sC94MUuEb0YrGWSJeQDtsicK1PmI7kaMsYEaxIgDL7YdByojIiTWLfClzzXIFYexAKpUyPLNICa9NWo8XWaAXaYhTR2ZBVeKc78vQXGDWxCn3qF2c/9PNu9573kHQU0LQgdIdfHextEbg0q5GbifOgpRNbA+7N9HxcMn5pKmIc86kKX6BE6KS8yNhq6du+NwIXApJQPllNYxNGqjkJLAZPdIT7+bEexmFq5wee5fwjH08OZ7bCTz2SiKJ3ef8fuYNvfCWFwLHK+zxgoTrs2T/ljr/830SpACcdDedZLNzZD93mzt39PlEdJvpqRYEHB7iswaY8KExGL+8WzOBa7snn3WDkYf19c/70jQ9TJs8aL8CgtQQsdJ+tS1AY5Cmv6Opy/1WjdmC+YVq6sAuBnQkb+m9W/nPy3zHOh0XSR0wapsktHn6xWzz2B5PBUR/xlp/c14Nv/E+3/NZfiAHtMG0miwLcf7d2OmyenJ18682yHjcCqdQd0H4hYjDkgULViDwgTtuM0s5h4M5LrIVarQHBQr28r/wHuF+xSAVFeS0I0ZwOeEhlK4MwgtR2FNk4pUNJupqW31suD1tn5AilvliGp3aZdz6UMuU2KGoT03VbAu6pt97sptOnnMulIDMXhNEfmYjNrZJVgQZexNYkKAnNRRcWzmOqmDJu2GqjcpmlIVX/GFVEbKwa8kOsL/OcdQvwbEeGNlBse1sbccdCwG3v5I0mJO+WABU/g3DWnp3nMYISgbRtTseFydlmjHvqLTe7aS4kX+Ro58O2myI/ZX/LH7g0iBaZK8wkCyBbrfE1SYZLAdyyKmbStAWKKHrCbU3gSS1Iwm+hQQEMaDgRtoutbNCGx4+cLsNHrOSRi0Pn9nQZNQ0kG8d+2aSbyEGA9F5JBFsE7vLM6Tj0wymvyLQQ3K0QX8IikITMYREG+OnvgXEZ05bWIMq3j/WpTGaFCg7arPO4g7ZFeQQfB20FFAItZkedp+2UNBZ/bLN5LcgxnE+xEWpfo7yQ3r8/55G3wbivcMszJ/mh2tLZGEJr7UHdrNifTEWUA8IJg9o/LkrWCvvTN/4tHuFb+tDwJh5+EXA7tQeQxfSg4vNX03UtUORPcaDySKPgw1xybEUVYTtuWIMIGNMMgnYOyftzHvlFG0uFk295RZQKQ5qWfCTRlGW5Q9LJUZTsEEF6lC5wD1IhAreGUmDgTUsTxWDzAe3sps0mhTXDuWf2RGLdc7zyE2Nbx5jW3Gbf1X2tXcIGAAJwZ8Z902ZZhRWBq+RBq28getfLB2RTmtNoTkrAfXBWxDTLRpiBjX9wCZgP2Nhp19rbY6cNYMBdqsbEwRogoemVUw4yGYvO5YeGNJuBe+nl7sIbtw1c5KTcfpHpLEnIJ2M4cqH79fNaW4n8qRuyVLBZi0646MCYX8UJHQOdRYPY1X0MSPnqmK5p3FvCCARu47yEpr4jjhFFespzG4PYqz0IjRxZrROyaX7trHEvvdw9ZKvARRmAdfO087hlIxGjhgpndu4UlinWCefpUf7UDQ8Hw6+32kTT1Y41WZrkgWvTpcbphIrQcMurka6Vnj7OtDGixfvbkz09sLgQSAMtRxvtbBS4Y5ovtHcrwL3XBY0753HpsRcFEegAv4GJWJJ1Cp+43JWhOyaR+1EPALc/O69s1piIJX1DNWLpCA4zbLUrhwndeStMcaeK9wk2E5OnAiZJAErTin6mgWX61wzvQr4U3bfaEnO5LdZGuWl/PtfhhZsx7hcTcOmBIAC0tfed3djFLiPRmzt3G1MKk9MgcEd1aUp5FbShIzQbbFRGx2LbwRAPswdtXcXDToNVBPhVuGrJAwIKrWn7bJuLnIJ5Z+BetiFwv3SvO3kbyirIxQUEWDGOsW98S1fTmTm4cQTHhBHezKTCUPZglGUB5ff27TfrIpA8qMbralriFONMa7ElYfciLwZmySZwkbYUAyvD7IMGXJny6kc4Kn/gbmHm3Qg/0XZas9p2qcCNoCJ6A7GsxZYogzBSMENYJtCI8ZGQrD5Vz7g8MPeHgfsMI8X+lz9iTZt/S+ZncpZtHB00lt1QOVWk97cB3CIV8p4zuRo2NvunCwt1REc20/Lns3qTKteSmTngE3DlZAwclQ71YZoACRT0vU47gK1pG2F7marDMjHmdhjfHGPNbNQf11qKaCwO0NGJF2CWhsbO1VjxXZbEEoXkz7ncPeTmDbbuBOC+Mi1AiOyBOd5AvxLD1v8cm8TJoaPRsHUclz91wyPA19NX1LTp7XqyMhY+zexB03gLt/P3nuUWj3qiqsfVbNrKO1K1icJY7yw0cr/Z3ngNGlTK3mzQtAhOD8mctnRHvv8fuvN+9PK1tw7tAeAW3+8dElO8mcum0uyhVOBIsQ0niowxf+r6R5Bxrg9KGx7SL+SA6lpauL0dpz/YKALQVrliHqocP15y/rXvcEef8eMEuNZo///6+zknip1tpMczcB8gjFvm1D3QBS9EgEUTTD4PiVegKjIRyTAJhM8czM8gwK1hkdODMRlT4UHOAC2D5t9bjcfF19Qxwgx0seOOX/ef3NGnP39kjA6vARYowD09H3qX9G1vAl1ChwRuf4IZNVej9LExh4mvjRGcABeB1gKenIiN3is7ZkzEWjpwbnzu3CFwN3bGCtwzjT2FGJDG5Nb8nkQsfB2v16WdK6qEOJU/df338DlL+cmQB0ysjeZpqdbtZg6KzMqarzY8d2feObBwx69/xyHjbgDfCNxX15WzFuMJQuGXdspRy8fJwcQut589kF+HahYKcMt9kPHQ/rCkVZjhOnvEkr7hE5X0gEaICsAtuZKUZzwE7gaQjbcG4N726phVYJkYnBExT3TshPiYo1XPjCnDwkpC45beYUkZgNsG7fyEWgSeZ3XQak1RbyWZBzRtQTrZanMI3O0CNwIrwUjOVxBJjeZ4O9+DMCN8axK3cP5klgoDTFsEsjRZR5fm7e9aFyUvszw2r6aEIxNFwcchcB8M4Or9dDwnzjdSDkymmh/1E7KTqoaQ4VBEWHPN/uR1WeOi8BAuzJE6PXcke0BmnKnaB8sDOzyE64s2AitOM3Cvm9Nhh1mFdRFcpEKpDuMTsUi/YvKd5V5Dk4ZLzOq+fvbBXowqkX/yJ697JCBClKcFIOukTnKJGvWkYuSBe+MbQWndfO9iBu7bD4G7Lmqpxj192ihr7JcX2vn7+cSLxlctgSwpMlQhkhBX+hsGLqmuMjVtL9/XqqfV0qJoK5pBiAlxWZqYPPYQuBtANt4aGfc1sB63eeAgmTixRoTBG015oWiL7tWgDXdqxm1sIS/UiXK86+ZpReojhRnItLRO9hC4WwDuH8asQpYKhFBUuktpPYyBsXMVNGj1uXQzYOf98hK48b0EuPZO3Mh86V+Lac2Om/fy4vFinIHdrmFydigVNkHv3hclcOVqGAFYayKW/ja+L1Bo6QCwsXWD3F8O3NIAROP9ukxW6Kws2ikCL4YZAG1oXlqAeMbzNhm7v9H3BuC+ec7jzmWNqxe8yPmKwnY4WD/pXJH2yj+GSrgBTcuW/CvjysM6UFmjLQ/Ce9MXCWHKC6bLonOklEVKIIxuZ5lv3UmMewjcdb1vBu6JN7/aOQjcNN4dps27eHNMZYeENOTFKhMxtO3fn7z2UbF2wUpvNEBX1IN5jb24QCdh8TnDoI2zVb9wFwSpcAjczYD7mnIgSJWE/ZRVwGRvi7qBKRu0AgcN6RmBW3K1eMWEG0auIxuyIkposB+j5gXjQf3W2bJop3ES5uHR3h37x1e6nSc9DZyYWFsMI4BigtbQo0hDrjdf0LlPvLI4cvl9inrm81GbV3inX7j9//1Vd/Y33++m3V3yMMS0+rm5UgtiwyLBzGOQ6FT2IJlE6u48OYvAxVvBe3WZ4ECN0JEeS5cjkcaZFgt/ckynFdIMbVXb2dDUJaQYgOjaZ5UyPvAOfUhfncwa381lDjCSsmShdhC0LaYtDcA2w6fao5SX4SxpTBLjVhasHlQYLzelLAdGXYruyUrHSJUkULMshZWnVYRinPAnAbv2hk8UbdBhHbbWz85Qm8RCrmIQpd0YGOV7UPQiX0EKl4/pUnMi1tKzaexKmGfjI9+7yvjb8oAHRm5L/8C1fycMNWvzgKfKWR4AvIBeAnprGRcydWlw9JcsQWqvOCAAw/L+dfZClft72Q3ECLmKDYCuGYmSqXofCmGDJPaIhed3nCq2obC2crBGiI9OKckKOTtxIIIALC2a516QI/bpe1IxeQFuk+IZ2o3UyUCVVxMU7WqjYSMn8Ofrc8ubmqwYuKG3ufuDY4NybYX8poE2ugwmwfTLRjpKvXtkN25+S2FDnTPXJJADcRFJNfUzuoVcO23SoqLbTdCGHsOT0ZPzBMYtT+zWD4A0Wbm5X0wcL0XhV3upmcxuhbOWntUTxTqQPZbtMVFiI9W0DIyATJzHLhFEaVZiE/Jg9n0OOjfp2qUCipFATx6UXSfCASm0KQYEPNdg2kju1KnyL4gNI3DbIZoc1oAFdy97UHWIBC3SZMjAeBCZRiRM25cGIJxB8LbbV8ZuKC3Ebcfw0vrMKGHbsQOpcd/GisDB+LZ2+7YcepoZE+3kbDOtiqyl/7xt/oFrMuMaHpVutLeQG3oujWr3fFrGJo0Dz1i4lNKlhkXNeiOatqdnRdilrNLdxo21cHhEAqxyNPb8GqWKmjOjnLBLKnipcon0A4YHfn/zIG1jPMpjIRl25YFm2hTNBJE7/8A1F4Mu0A5IQU4eMTMNt3rNPLCnji4uoHc1WI9IgzHACjYKN422TTNZ6/zWGu/sKBW/I0b/YYek8qACo00YuHjfdnjZEnQKOLtGyLISfQygWcUy7D50OqchXwFwA0OFlTyac1Ne29XDuUkjmtZyDgO0gnrW0sPBwAi0CaCNMBj+1DrzoTJibmlRS6FH6SMh0NnYSNZJWOWHAflCSkohm7dCvLkiJhwFMKpdq4JLE3larvv8aMv0XgFcoi9N3TaWdomdGGWzkYJlwnjJ8Guddg21rGA9hShp1JEMALGlpDOlaS0mlCmvEdnTOenSiJDFX/KsnaVIUfvqPsfqazjXrKKKkIfx7zrKS7MFvihZBSoVClgNBoThgQ9q1rRmPa0AhQYf8jwNWjOP3FvNCr1HTsWYLDElCPOwQF4OrL4vPBDmaVV/p/q1PLptCjGtHPD4s8nkHabNk2x+f5vh87W4ymtuSv7yH0+X1qBpSB4gYPOhiPMd/oFrHl0vWW3XQnKI2jH+Lsq2yhNDu8eq7PHgFPzVsGzWRZRLWlGAi63ctxrq6945khi326bat+I3xCog4oJBs0CbpKwUoYKQrrfbrH0kUsEAbx+ahAlbpTsZBqwon3CZ+8aBS8IvoujxlNlIyqt2ooLdCpc6TGvQosUP9LyR7MEoy7ZBy9o4mO6i0qrYBQJvhGkHGDqFaO0U6Pn8d7V9KLdv1x4Qh4yRv6TMjHrt1H9JwJVxm2wLBpPuww/3rjbRWSmcEYkC70NhhWkmJA2yyxuhqjCCpemlfBGSKXs/BG26l7e7hFXmzEN9Q9KuHeJrFBqRFlj64AnqGhOxVo2FOPC5sO4sFXDFTnsJN8X69P8MYIwUvMBGE6OX2DdUZKO1aRl4EAkUKAD4WqG2OlSRFJxpUXi0IovczTwAPMFG8Q5iJ9Y/g3zUSeDyvei+egAdj84StMiZAVEwsZoB0d506R+4+jHhNjWGAyFqlcWF+I5sWrAOTVi1yM1STs9BW7A8NBFbLRLQd0cYGA5TjKYHJ7bPp7QXHdoUDpkQBSmv3kocfzfW3C2nJJqRj33oS0yGln/1PzNLmSWmtKsEP9mrY5OovaztXDHKtaJrAO5KoE0fr4olZauBYizMa4/nHYdhVqRTwjX6OPtsuB7T9pwWOFkBfH6HtbgA5AH/1YA2Je/v34s0ewMUzKnKvRGz4X87KS9hG9i+YizM5vYnFeq7/YnEuIzyjeR69bh89Xi9KvQ6jWRwYLAd4hArVO2G5Mto+G2cd9UDbSt7oPoL5AGyidLrnAVjnwf6BhcXkHTRNsfFMgAHUB6OMm0FZjUDllYAuI0kN9NDI9mD+NLciLi1jRReIBC0PHY4hIa2ZV+p+B5e7QM6jMVUNLC19oCRgAJd/Kt50Am7WbeD6gI1uAz0oI2t/rfunQtmwuG2vGetZVyTafPOX4O02gxdHaACN4dHFiq0J8cHj6SV8ApOWEk2NFQ1DDvSPw50oy6Cecfgd3vRztFWlRt/B2A8xbTZkJIJQS2tKrkUoAPSpUaw/kdCYqTkJMJY2nDKONaWdLFTXuxxQ5q2UevLQnW1iwYu8vgm06bOKQ8ywhnybK4j9P63XmV/eaZRFyFOHIyjIcGxWlqIRBE3rZCnpWGgAKNhu+gwDWnAUaIK3MOjW5NYk2mj45knJrIP9RrSQmLJkEH1cEN6Q9t5/YmrHwu+uiPYsgkMrEEboSK1jmuXOKDohHNDwCsmGK+LoObJA6sCjXo+b0cBbm8ZV9iO2iW+UzKm1nTr1tKaac6OHi5jAYGW7Fz+xp2q2pHay1pcWD0K5LEDwK0PC40wQWtV9MuVFOQ5PARV+wCQ9lJe4eYx6aJZdmQSRtpKhXMALP+bFX5nPZt7phzaYrz0ewpaBQqTLfO4rTYWNYokDLDnr1lLC48oWC+K0MDsT1z1WOFXtdHRUKOgiIPIoz7SR5hRIOP1QmgIg6PtE4zZ+kq3ofmyt+McrdCDpEMZuNU2I1VeiI0szTkie9S9Kl1YzK1OJWqDlvWLhbNGtBRjq0jAZPT4TAHcCrT6XCN7QAaXhnkOQAlcBdry0eJ8XzwkZORbEtlCxooYawgIZ0O6DxveXFxQepO3DYfudkQaZOiMnWiU1m7cFkuPTMSgZpbRBxEWQfSwZscROHQ2Mi6XB/E06YZm7IGicYJNNGxRKoMrdmhw12daXR2FmMwA7cBELKJI1tKO1THTnQv9KKSjSO5bm0C0/MEZlb48yPKI+cNgLTeTiL3oKivEMnDDfeXMA6QbLS8aKQLnTJvDAjVu/O9+agfX0iaF0tPDvS8agvBU5QHcQp6YTh4XhTTtWN9wcXznKKs0dmZtc4NlC49AsJGJWBgg4SiWxlcgrHUvhpSs8tp4pjzbzp+46pKI2fIyYxm3AQoVzpBGJBetVYeL9SwEDpqE5ZOyNYvJAeHRh8qg/KHoGvSkM4O6gzwQLUZpRCBcoM2lT8jHkE+PMyZDY8FqU8K9ot5hlGll9snQtC1p0LBLQbOqW/BBKuy5yR+J/VsNtNqx2qmdzLRUv+dQAyoqRE7SOqwjhb2WdjM/oEKBZ0mDOWkhC2aUVmeZjbYu5WE6a1JoS0r3xWjCscKNqO2N+UUB7np1B2U+wirR1gCt6p98hlkhtu9PXPW4b7tpekRT0zKkNcIF8h7GtMjIIzNsJF0QYPXzYzgxjFqkifH3/pFIOfIV4Cq2Y4wn2iekCTNfb3m6PHcctKSxkSda8kA8H0bVAjwLtLy/MdLSVrD5jviDPMaUveM7/q+vvOSPvVs8BbKgYXTaibJo0GC8ONlDE6yG0Zk3GsU8LO6DZ/U+WWSyVXw5XhEjg0HkFQcFmg+g/sdnKUD1loDTuJgLEx0mGzpdxgSu7EefaSuMUKQyMJBsC4nATX/iT1x5ya9PbvFPOHA1C+Iw3xggAWRFxpss4w5M4rqrRg0mDm1dhuR9Z39ZjAR9aQAijZglF/t3J5irTGKN6KgGo69p4fg39ojRgvaIJp8OLGQCGy9RJ80OAR/Gxt3lT1z5fbdObnqzc24nGs8GYx98yKMQ23aYdorZ3FW2tzOSaR7rn9+NBrWeeaAjEGZaFZTW3tHRtn3tHxofdG/pXyX0FGH4OKI8eLxFRgK2JG9Fq1aUUwDSUWggihwsJ/cv/Hdf/vhnOr/8qHPu4hgf5ek0cjWsYaTc01KUk79LSJiLe2m6gwLeAm1nwNLIdpmWHLNJwVls2tuNG2yEKrySXTqSiebM6/uxw4PsCGcuTjRCI0rHRPZr11PX4VxNHkA93LJLrv6DbMGwMV/5zQO38yL/zZf+gwuPH/nrDzvnf0Ks1zIaV1oDNYTpkrZ2yTgrnzRkz1uvYGbsXIDarvzKIJUhYC0WHFlcwJqe+DYZprYmDu3s1SIb1VosxFsSofzekBalpfZWm6ipetucgGRKztfbfl9OsHHut/z+4qdDS7/78u99qXP+Pc65Yxz0sSNDoE254AACMRELv1tb044zLawuY6Gbgza2Nf8z9ogpLSCWcY1CccaWBBiQjdQ7BIBSWM6/tccDkYVle1lPq+/lZx5YoENLzOJawzGi5VEtLopA4Xf7y+Xy2ks+8bvvCz+duO5Jj5x2z/5X5/xl5WEEwbbXElAVFEiwDyx1lo6tybRVV/FwaUx0FPHMxhs6gC5OxBjghx3SIAEGWgC8BFrYZnBvNUBiP4tljfcqp8g4MBh/nJCAY8C5iARt/dk7/wd+37/w4k996i/Lb7/7su/7p5N3t3vnLmBbnLmO0uHNMF69cN08rel1LCjEMArYoLPSZ8sDFOLn5wNduwJo+8BDfYjRqzoKsom+rxBNC7Qj42bZlkYpJg9J9Oo45CDgaQXb6YMDd+3jPnnPh1iPv37dYy64cPfYrzjnXtY3MtZk6r6B1I5Re4DBSIuByASQIVmFbhQCE2u2CmaE5t460ypjaYdpzrAb4Xc8T8ulU462SkJpQJQQr1ROpwA/v6OpaUHfls5/cLFz/vWP+djH5s9gcjF64p8/8UnLxXJG9FOZZGANbzMhCzWtwWnJgx6TldoKCUo7zJSIlw6fy8u4ZmgMI1KlQR2gxrI29SCyvq4Y07Jnjm6TONpARj0hy1jfeCERkU60YCZlqFuTqdBhrJlRJVlvgSk0hETGYXKMfb/3YOFe8rjfuvvPBKdXi//VlY9/rp+m9zq3eEL4bRO03GOLPQeAZzItrgnlH97oz7Dh+n3oymD2II64XFxQg5mAgR1GsfTAwgkFhXKqBuCH5IHB0uVe5gHtmmgOPBSBgYQhwBVREnhqtqn/2r7zV37vb3/qM/Qe6FLffdkTnz/56e1uchG81OMZmEX6I4xU6TDxdjmwjVpaajz53sJIwp8kI4msBgPQQD1tbAKo9OpJnzSaYwdN48kKH1Bh+x7wFI3JzIEGUxxO2pb26TJqeMzic/7MzLgQ8AJT5B33T37nlZd8/JMfl3axYoH7q5c94TneTb/knH8qWk0bK020JhNrFIH3wowxqNUIMXOg9JsaCSYPkr2xpmfGFO9XA9Rhy1iW2JM+OQbWsynCYyGTCdACeaGYlhCDciBgf72ShhyDE5mKIsz+DOz3Huy4Wx73sbt/DzmzCdz54u++/Ik/sJzcG72bLnfOX0B2Log1/E7Kiw1ae7WmNrKChXYWAg+AgodplO4iRi73V2nQ1qVigIDTMFA0QFsYT7Fley5RSQp981fWHuhJWPwNjZhGuE+AVc0bmoQlO1kH62HQnnaT//WlX77pkt/+9FdwBMKFnOzasLK2OPmCaXKvnpz/Ye/cUcbspm6JhRWZIzbZiavDGQqhIPTmy1bMHnBjDUzGoESAQNDnma37OabmMmmZiCX/1cDlQ29p1M7ignbIzBfRhKqQSIwRd/hdN/nPueX0Tnfs+G/k7MHawM03PvDSv/uofeee57z/Se+mp03O/W3n/E63AJywmQIE8wAGxmKAsXwfN0h5ZW8ixt4PFhd65YUEsCoSaIrSS+jDs3YOvPCuXi0t6xvhD1R0bk2m4a6KyqIqOrIBbh8TmoB94Jz/lp/856Zp+V+Wx93vXPLRT3/bAiv9fVMqoAcEBvYP/NByWlzm3OJZzk1Pds5f7Nx0kXPuCF7qNHbiio6q9yWDKr0IBkXei2sPLI9HW25GmXbFLfmFiRIALBsA+VG6rYEGvrWAn6/8ySxNRCwtnmmtpqVFk8i4lZC8c3uTcw+45fQN5xdfnvzyD5bTkc8e3Xf3XfypT50aAWy+5v8BUrIHNHvQF7oAAAAASUVORK5CYII=", - }, - typography: { - fontFamily: `"Roboto", "Helvetica", "Arial", sans-serif`, - useNextVariants: true, - h1: { - fontSize: 40, - }, - h4: { - fontSize: 30, - fontWeight: 500, - }, - h6: { - fontSize: 22, - }, - body1: { - fontSize: 18, - }, - }, - overrides: { - MuiMenu: { - list: { - backgroundColor: "#383B40", - }, - }, - MuiCssBaseline: { - '@global': { - /* - scrollbarColor: "#6b6b6b #2b2b2b", - "&::-webkit-scrollbar, & *::-webkit-scrollbar": { - backgroundColor: "#2b2b2b", - }, - "&::-webkit-scrollbar-thumb, & *::-webkit-scrollbar-thumb": { - borderRadius: 8, - backgroundColor: "#6b6b6b", - minHeight: 24, - border: "3px solid #2b2b2b", - }, - "&::-webkit-scrollbar-thumb:focus, & *::-webkit-scrollbar-thumb:focus": { - backgroundColor: "#959595", - }, - "&::-webkit-scrollbar-thumb:active, & *::-webkit-scrollbar-thumb:active": { - backgroundColor: "#959595", - }, - "&::-webkit-scrollbar-thumb:hover, & *::-webkit-scrollbar-thumb:hover": { - backgroundColor: "#959595", - }, - "&::-webkit-scrollbar-corner, & *::-webkit-scrollbar-corner": { - backgroundColor: "#2b2b2b", - }, - */ - } - }, - }, -}); - -export default theme; diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index 9f1fa983..e871ebbe 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -1,11 +1,11 @@ import React from "react"; -import { createTheme } from "@mui/material/styles"; +import { createTheme, adaptV4Theme } from "@mui/material/styles"; -const theme = createTheme({ +const theme = createTheme(adaptV4Theme({ palette: { primary: { main: "#F86743", - contrastText: "#ffffff", + contrastText: "#ffffff", }, secondary: { main: "#e8eaf6", @@ -71,34 +71,20 @@ const theme = createTheme({ }, }, MuiCssBaseline: { - '@global': { - /* - scrollbarColor: "#6b6b6b #2b2b2b", - "&::-webkit-scrollbar, & *::-webkit-scrollbar": { - backgroundColor: "#2b2b2b", - }, - "&::-webkit-scrollbar-thumb, & *::-webkit-scrollbar-thumb": { - borderRadius: 8, - backgroundColor: "#6b6b6b", - minHeight: 24, - border: "3px solid #2b2b2b", - }, - "&::-webkit-scrollbar-thumb:focus, & *::-webkit-scrollbar-thumb:focus": { - backgroundColor: "#959595", - }, - "&::-webkit-scrollbar-thumb:active, & *::-webkit-scrollbar-thumb:active": { - backgroundColor: "#959595", - }, - "&::-webkit-scrollbar-thumb:hover, & *::-webkit-scrollbar-thumb:hover": { - backgroundColor: "#959595", - }, - "&::-webkit-scrollbar-corner, & *::-webkit-scrollbar-corner": { - backgroundColor: "#2b2b2b", - }, - */ - } + MuiCssBaseline: { + styleOverrides: ` + @font-face { + font-family: 'roboto'; + font-style: normal; + font-display: swap; + font-weight: 400; + src: local('roboto'), local('roboto'), format('truetype'); + unicodeRange: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF; + } + `, }, + }, }, -}); +})); export default theme; diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 8361de43..da09b35a 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -2343,22 +2343,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
) : (
- {/* - - { - console.log("Should go to icon") - }} - > - - - - */} {userdata.support === true ? @@ -2407,6 +2391,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user style={{}} aria-label={"Copy orgid"} > +
{ @@ -2436,6 +2421,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user > +
{selectedOrganization.defaults !== undefined && selectedOrganization.defaults.documentation_reference !== undefined && selectedOrganization.defaults.documentation_reference !== null && selectedOrganization.defaults.documentation_reference.includes("http") ? +
+
: null} {selectedOrganization.name.length > 0 ? ( @@ -2764,8 +2752,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }; return ( - - + +
+ +
); })} @@ -3107,6 +3097,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user style={{}} aria-label={"Copy APIkey"} > +
{ @@ -3143,6 +3134,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user style={{ color: "rgba(255,255,255,0.8)" }} /> +
) } @@ -3715,17 +3707,19 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user title="Set in EVERY workflow" placement="top" > - { - editAuthenticationConfig(data.id); - }} - > - - +
+ { + editAuthenticationConfig(data.id); + }} + > + + +
) : ( +
{}} @@ -3741,6 +3736,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user color={data.defined ? "primary" : "secondary"} /> +
)} +
+
} /> diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e45902e5..0c96a399 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -51,7 +51,7 @@ import { IconButton, Menu, Input, - Fade, + Collapse, FormGroup, FormControlLabel, Typography, @@ -119,14 +119,14 @@ import { import * as cytoscape from "cytoscape"; import * as edgehandles from "cytoscape-edgehandles"; -import * as clipboard from "cytoscape-clipboard"; +//import * as clipboard from "cytoscape-clipboard"; +//import undoRedo from "cytoscape-undo-redo"; +//import cxtmenu from "cytoscape-cxtmenu"; import CytoscapeComponent from "react-cytoscapejs"; -import undoRedo from "cytoscape-undo-redo"; import Draggable from "react-draggable"; import cytoscapestyle from "../defaultCytoscapeStyle.jsx"; -import cxtmenu from "cytoscape-cxtmenu"; import { validateJson, GetIconInfo } from "./Workflows.jsx"; import { GetParsedPaths } from "./Apps.jsx"; @@ -232,9 +232,9 @@ const inputColor = "#383B40"; // http://apps.cytoscape.org/apps/yfileslayoutalgorithms cytoscape.use(edgehandles); -cytoscape.use(clipboard); -cytoscape.use(undoRedo); -cytoscape.use(cxtmenu); +//cytoscape.use(clipboard); +//cytoscape.use(undoRedo); +//cytoscape.use(cxtmenu); // Adds specific text to items //import popper from 'cytoscape-popper'; @@ -12880,11 +12880,11 @@ const AngularWorkflow = (defaultprops) => { {defaultReturn} : - +
{defaultReturn}
-
+ ); //return null; @@ -14960,7 +14960,7 @@ const AngularWorkflow = (defaultprops) => {
) : ( - + { setCy(incy); }} /> - + )}
{executionModal} diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 8086e1e7..49afb8ed 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -1893,27 +1893,27 @@ const Apps = (props) => { filteredApps.length > 0 ? (
{filteredApps.map((app, index) => { - if (firstLoad) { - appDelay += 75 - } else { - //return returnData + if (firstLoad) { + appDelay += 75 + } else { + //return returnData return - } + } return ( - - - - - - ) + +
+ +
+
+ ) })} {cursearch.length > 0 ? null : searchableApps.map((app, index) => { return ( - - ) + + ) })}
) : ( diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index dfe028fa..29b0098d 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import { useInterval } from "react-powerhooks"; import AppFramework from "../components/AppFramework.jsx"; -import { makeStyles, useTheme } from "@mui/styles"; +import { makeStyles, } from "@mui/styles"; // nodejs library that concatenates classes import classNames from "classnames"; import theme from '../theme.jsx'; diff --git a/frontend/src/views/DashboardViews.jsx b/frontend/src/views/DashboardViews.jsx index a23a3566..950ae73e 100644 --- a/frontend/src/views/DashboardViews.jsx +++ b/frontend/src/views/DashboardViews.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { useInterval } from "react-powerhooks"; -import { makeStyles, useTheme } from "@mui/styles"; +import { makeStyles, } from "@mui/styles"; // nodejs library that concatenates classes import classNames from "classnames"; import theme from '../theme.jsx'; diff --git a/frontend/src/views/SettingsPage.jsx b/frontend/src/views/SettingsPage.jsx index bbac998b..6ef29612 100755 --- a/frontend/src/views/SettingsPage.jsx +++ b/frontend/src/views/SettingsPage.jsx @@ -12,8 +12,6 @@ import { } from "@mui/material"; import { useAlert } from "react-alert"; -import detectEthereumProvider from "@metamask/detect-provider"; - const Settings = (props) => { const { globalUrl, isLoaded, userdata, setUserData } = props; const alert = useAlert(); @@ -814,65 +812,6 @@ const Settings = (props) => { ) : null}
- {/*userdata !== undefined && - userdata.eth_info !== undefined && - userdata.eth_info.account !== undefined && - userdata.eth_info.account.length > 0 ? ( -
- Network: TBD - - Address: {userdata.eth_info.account} - - {loadedWorkflowCollections.length > 0 ? ( - - Collections:  - {loadedWorkflowCollections.map((data, index) => { - var collectionname = data.toLowerCase(); - collectionname = collectionname.replaceAll("#", ""); - collectionname = collectionname.replaceAll(" ", "-"); - - return ( - - - {data} - -   - - ); - })} - - ) : null} - -
- ) : ( - - )*/}
@@ -986,137 +925,6 @@ const Settings = (props) => { //saveWorkflow(workflow); } - const handleEthereumTokenCreation = async () => { - const provider = await detectEthereumProvider(); - if (!provider) { - console.log("Please install MetaMask!"); - alert.error( - "Please download the MetaMask browser extension to authenticate fully!" - ); - return; - } - - if (!provider.isMetaMask) { - alert.error("Only MetaMask is supported as of now."); - return; - } - - if (!provider.isConnected()) { - alert.error("Metamask is not connected."); - return; - } - - console.log("Should make a token"); - }; - - const handleEthereumConnection = async () => { - const provider = await detectEthereumProvider(); - if (!provider) { - console.log("Please install MetaMask!"); - alert.error( - "Please download the MetaMask browser extension to authenticate fully!" - ); - return; - } - - if (!provider.isMetaMask) { - alert.error("Only MetaMask is supported as of now."); - return; - } - - // Find the ethereum network - // Get the users' account(s) - //alert.info("Connecting to MetaMask") - //console.log("Connected: ", provider.isConnected()) - - if (!provider.isConnected()) { - alert.error("Metamask is not connected."); - return; - } - - provider.on("message", (event) => { - alert.info("Ethereum message: ", event); - }); - - /* - params: [ - { - from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155', - to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567', - gas: '0x76c0', // 30400 - gasPrice: '0x9184e72a000', // 10000000000000 - value: '0x9184e72a', // 2441406250 - data: - '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675', - }, - ] - */ - - // https://docs.metamask.io/guide/rpc-api.html - // Gets accounts - requires previous permissions - //const method = "eth_accounts" - //const params = [] - // - // Asks for permission, and gets the accounts - var method = "eth_requestAccounts"; - var params = []; - provider - .request({ - method: method, - params, - }) - .then((result) => { - if (result !== undefined && result !== null && result.length > 0) { - userdata.eth_info.account = result[0]; - - // Getting and setting balance for the current user - method = "eth_getBalance"; - params = [userdata.eth_info.account, "latest"]; - provider - .request({ - method: method, - params, - }) - .then((result) => { - if ( - result !== undefined && - result !== null && - result.length > 0 - ) { - userdata.eth_info.balance = result; - userdata.eth_info.parsed_balance = result / 1000000000000000000; - console.log(userdata.eth_info); - setUserData(userdata.eth_info); - - // Updating - //if (userdata.eth_info !== userdata.userdata.eth_info) { - //} - - setUser(userdata.id, "eth_info", userdata.eth_info); - userdata.userdata.eth_info = userdata.eth_info; - } else { - alert.error("Couldn't find balance: ", result); - } - // The result varies by RPC method. - // For example, this method will return a transaction hash hexadecimal string on success. - }) - .catch((error) => { - // If the request fails, the Promise will reject with an error. - //setEthInfo(userdata.eth_info) - alert.error("Failed getting info from ethereum API: " + error); - }); - } else { - alert.error("Couldn't find any user: ", result); - } - }) - .catch((error) => { - // If the request fails, the Promise will reject with an error. - alert.error("Failed getting info from ethereum API: " + error); - }); - - // Gets the users' balance in WEI (one quintilionth ETH) - }; - const loadedCheck = isLoaded && !firstrequest ? (
{landingpageData}
diff --git a/frontend/src/views/Welcome.jsx b/frontend/src/views/Welcome.jsx index ebdd5036..7cafddfd 100644 --- a/frontend/src/views/Welcome.jsx +++ b/frontend/src/views/Welcome.jsx @@ -11,7 +11,7 @@ import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; import { Grid, Container, - Fade, + Collapse, Typography, Paper, Button, @@ -417,7 +417,7 @@ const Welcome = (props) => { App Framework - + { inputUsecases={usecases} setInputUsecases={setUsecases} /> - + } : - +
{/*
@@ -537,7 +537,7 @@ const Welcome = (props) => {
-
+ } ) diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 30982e9d..890ee35d 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -8,7 +8,7 @@ import { Navigate } from "react-router-dom"; import SecurityFramework from '../components/SecurityFramework.jsx'; import EditWorkflow from "../components/EditWorkflow.jsx" -import { ShepherdTour, ShepherdTourContext } from 'react-shepherd' +//import { ShepherdTour, ShepherdTourContext } from 'react-shepherd' import Priority from "../components/Priority.jsx"; import { isMobile } from "react-device-detect" @@ -3459,10 +3459,11 @@ const Workflows = (props) => { return returnData } + /**/ return ( - - {returnData} - + + {returnData} + ); })} @@ -3499,13 +3500,11 @@ const Workflows = (props) => { /> : null} + {/**/}
{view === "grid" ? ( - - - - + {filteredWorkflows.map((data, index) => { // Shouldn't be a part of this list if (data.public === true) { @@ -3522,12 +3521,11 @@ const Workflows = (props) => { ) } + /**/ return ( - - - - - + + + ) })} diff --git a/functions/onprem/orborus/build.sh b/functions/onprem/orborus/build.sh index 4af6ba6c..60161e59 100755 --- a/functions/onprem/orborus/build.sh +++ b/functions/onprem/orborus/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-orborus -VERSION=1.2.1 +VERSION=1.2.2 echo "Running docker build with $NAME:$VERSION" #docker rmi frikky/shuffle:$NAME --force diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh index 28d6a47b..a379f69c 100755 --- a/functions/onprem/worker/build.sh +++ b/functions/onprem/worker/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-worker -VERSION=1.2.0 +VERSION=1.2.1 echo "Running docker build with $NAME:$VERSION" #CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin . From 2fec45b47e492e12628f877a45330fdf83c63cd1 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 13 Jul 2023 22:58:51 +0200 Subject: [PATCH 008/160] Fixed most of the muiV5 / react 18 version problems --- backend/app_sdk/app_base.py | 21 +- frontend/package.json | 5 +- frontend/src/components/Header.jsx | 16 +- frontend/src/components/OrgHeader.jsx | 17 +- frontend/src/components/OrgHeaderexpanded.jsx | 2 + frontend/src/components/ScrollToTop.jsx | 2 +- frontend/src/index.js | 4 +- frontend/src/views/Admin.jsx | 207 ++---------- frontend/src/views/AngularWorkflow.jsx | 9 +- frontend/src/views/Workflows.jsx | 299 +++--------------- 10 files changed, 109 insertions(+), 473 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index b0ffee23..3078e65b 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -3556,15 +3556,15 @@ class AppBase: if "the JSON object must be" in errorstring: self.logger.info("[ERROR] Something is wrong with the input for this function. Are lists and JSON data handled parsed properly (0)? the JSON object must be in...") - try: - e = json.loads(f"{e}") - except: - e = f"{e}" + #try: + # e = json.loads(f"{e}") + #except: + # e = f"{e}" newres = json.dumps({ "success": False, "reason": "An exception occurred while running this function (1). See exception for more details and contact support if this persists (support@shuffler.io)", - "exception": e, + "exception": f"{type(e).__name__} - {e}", }) break elif "got an unexpected keyword argument" in errorstring: @@ -3587,15 +3587,16 @@ class AppBase: except Exception as e: self.logger.info(f"[ERROR] Something is wrong with the input for this function. Are lists and JSON data handled parsed properly (1)? err: {e}") - try: - e = json.loads(f"{e}") - except: - e = f"{e}" + #try: + # e = json.loads(f"{e}") + #except: + # e = f"{e}" newres = json.dumps({ "success": False, "reason": "An exception occurred while running this function (2). See exception for more details and contact support if this persists (support@shuffler.io)", - "exception": e, + "exception": f"{type(e).__name__} - {e}", + }) break diff --git a/frontend/package.json b/frontend/package.json index d463173b..11ce8c98 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,8 +36,6 @@ "jss-nested": "^6.0.1", "jss-props-sort": "^6.0.0", "jss-vendor-prefixer": "^8.0.1", - "material-icons": "^0.7.7", - "material-icons-react": "^1.0.4", "material-ui-chip-input": "^2.0.0-beta.2", "md5-file": "^4.0.0", "mdbreact": "^4.21.1", @@ -60,13 +58,12 @@ "react-driftjs": "^1.2.2", "react-dropzone": "^14.2.3", "react-ga4": "^2.0.0", - "react-iframe": "^1.8.0", "react-instantsearch-dom": "^6.28.0", "react-json-pretty": "^2.2.0", "react-json-view": "^1.21.3", "react-markdown": "^8.0.7", "react-markdown-github": "^3.3.1", - "react-powerhooks": "0.0.7", + "react-powerhooks": "^0.0.7", "react-router": "^6.14.1", "react-router-dom": "^6.14.1", "react-scripts": "^5.0.1", diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index f314e151..6a1f2436 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -688,7 +688,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho {/* */} - Workflows + Workflows
@@ -698,24 +698,17 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho {/* */} - Apps + Apps - {/* - - -
Dashboard
- -
- */}
{/* */} - Docs + Docs
@@ -796,8 +789,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho : } + {/* Show on cloud, if not suborg and if not customer/pov/internal */} + {isCloud && (userdata.org_status === undefined || userdata.org_status === null || userdata.org_status.length === 0) ? + + + + + + : null} + {userdata === undefined || userdata.app_execution_limit === undefined || userdata.app_execution_usage === undefined || userdata.app_execution_usage < 1000 ? null : -
{ +
= 0.9 ? "#f86a3e" : null, }} onClick={() => { + console.log(userdata.appe_execution_usage/userdata.app_execution_limit) if (window.drift !== undefined) { window.drift.api.startInteraction({ interactionId: 326905 }) + navigate("/pricing") } else { console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) } diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index a5665c61..2b2996cc 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -1,5 +1,6 @@ import React, { useRef, useState, useEffect, useLayoutEffect } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; +import { useTheme } from "@material-ui/core/styles"; import theme from '../theme.jsx'; import { useAlert } from "react-alert"; @@ -36,8 +37,8 @@ import { Breadcrumbs, CircularProgress, Switch, - Collapse, -} from "@mui/material"; + Fade, +} from "@material-ui/core"; import { LockOpen as LockOpenIcon, SupervisorAccount as SupervisorAccountIcon, @@ -70,6 +71,7 @@ const registeredApps = [ "todoist", "microsoft_sentinel", "microsoft_365_defender", + "google_chat", "google_sheets", "google_drive", "google_disk", @@ -260,6 +262,16 @@ const AuthenticationOauth2 = (props) => { admin_consent, "consent", ) + } else if (selectedApp.name.toLowerCase().includes("google_chat") || selectedApp.name.toLowerCase().includes("google_hangout")) { + handleOauth2Request( + "253565968129-6pij4g6ojim4gpum0h9m9u3bc357qsq7.apps.googleusercontent.com", + "", + "https://www.googleapis.com", + ["https://www.googleapis.com/auth/chat.messages",], + admin_consent, + "consent", + ) + } else if (selectedApp.name.toLowerCase().includes("jira_service_desk") || selectedApp.name.toLowerCase().includes("jira") || selectedApp.name.toLowerCase().includes("jira_service_management")) { handleOauth2Request( "AI02egeCQh1Zskm1QAJaaR6dzjR97V2F", diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 21d1c096..9b4a344d 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -1,23 +1,17 @@ import React, { useEffect } from "react"; -import theme from '../theme.jsx'; -import { makeStyles } from "@mui/styles"; - -import { - Tooltip, - Grid, - Button, - TextField, - Typography, - IconButton, -} from "@mui/material"; +import { makeStyles } from "@material-ui/styles"; +import { useTheme } from "@material-ui/core/styles"; +import Tooltip from "@material-ui/core/Tooltip"; +import Grid from "@material-ui/core/Grid"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; import { useAlert } from "react-alert"; - -import { - ExpandLess as ExpandLessIcon, - ExpandMore as ExpandMoreIcon, - Save as SaveIcon, -} from "@mui/icons-material"; +import IconButton from "@material-ui/core/IconButton"; +import ExpandLessIcon from "@material-ui/icons/ExpandLess"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import SaveIcon from "@material-ui/icons/Save"; const useStyles = makeStyles({ notchedOutline: { @@ -39,6 +33,7 @@ const OrgHeader = (props) => { handleEditOrg, } = props; + const theme = useTheme(); const alert = useAlert(); const classes = useStyles(); @@ -101,10 +96,9 @@ const OrgHeader = (props) => { //console.log("USER: ", userdata) const orgSaveButton = ( -
-
); diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 6e0a872e..f0a61a3a 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -9,13 +9,13 @@ import { Grid, Card, Switch, -} from "@mui/material"; +} from "@material-ui/core"; import Priority from "../components/Priority.jsx"; import { useAlert } from "react-alert"; const Priorities = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, } = props; const [showDismissed, setShowDismissed] = React.useState(false); const [showRead, setShowRead] = React.useState(false); @@ -60,6 +60,8 @@ const Priorities = (props) => { globalUrl={globalUrl} priority={priority} checkLogin={checkLogin} + setAdminTab={setAdminTab} + setCurTab={setCurTab} /> ) }) diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 8d571d79..15f71754 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from "react"; +import ReactGA from 'react-ga4'; import theme from "../theme.jsx"; import { useNavigate, Link } from "react-router-dom"; import { @@ -9,7 +10,7 @@ import { Button, Grid, Card, -} from "@mui/material"; +} from "@material-ui/core"; // import magic wand icon from material ui icons import { @@ -19,9 +20,10 @@ import { import { useAlert } from "react-alert"; const Priority = (props) => { - const { globalUrl, userdata, serverside, priority, checkLogin, } = props; + const { globalUrl, userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, } = props; + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); const changeRecommendation = (recommendation, action) => { const data = { @@ -101,14 +103,27 @@ const Priority = (props) => {
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 30f9a5ab..b7f42b03 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1,9 +1,16 @@ import React, { useState, useEffect } from "react"; -import { useNavigate, } from "react-router-dom"; +import { makeStyles } from "@material-ui/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 CacheView from "../components/CacheView.jsx"; import theme from "../theme.jsx"; - +import AddIcon from "@mui/icons-material/Add"; +import ClearIcon from '@mui/icons-material/Clear'; +import StorageIcon from '@mui/icons-material/Storage'; +//import ToggleButton from '@mui/material/ToggleButton'; import { FormControl, InputLabel, @@ -12,6 +19,7 @@ import { Checkbox, Card, Tooltip, + FormControlLabel, Typography, Switch, Select, @@ -27,6 +35,7 @@ import { ListItem, ListItemText, ListItemAvatar, + ListItemSecondaryAction, IconButton, Avatar, Zoom, @@ -35,38 +44,54 @@ import { DialogActions, DialogContent, CircularProgress, - InputAdornment, -} from "@mui/material"; + Box, + InputAdornment, +} from "@material-ui/core"; + +import { Autocomplete } from "@mui/material"; import { Edit as EditIcon, FileCopy as FileCopyIcon, SelectAll as SelectAllIcon, + OpenInNew as OpenInNewIcon, + CloudDownload as CloudDownloadIcon, Description as DescriptionIcon, - Code as CodeIcon, + 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, - Storage as StorageIcon, -} from "@mui/icons-material"; + Visibility as VisibilityIcon, + VisibilityOff as VisibilityOffIcon, +} from "@material-ui/icons"; import { useAlert } from "react-alert"; +import Dropzone from "../components/Dropzone.jsx"; +import HandlePaymentNew from "../views/HandlePaymentNew.jsx"; import OrgHeader from "../components/OrgHeader.jsx"; import OrgHeaderexpanded from "../components/OrgHeaderexpanded.jsx"; import Billing from "../components/Billing.jsx"; import Priorities from "../components/Priorities.jsx"; import Branding from "../components/Branding.jsx"; import Files from "../components/Files.jsx"; +import { display, style } from "@mui/system"; //import EnvironmentStats from "../components/EnvironmentStats.jsx"; +const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#f85a3e !important", + }, +}); + const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { @@ -79,10 +104,37 @@ const MenuProps = { 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, checkLogin } = props; var to_be_copied = ""; + const classes = useStyles(); let navigate = useNavigate(); const [firstRequest, setFirstRequest] = React.useState(true); @@ -98,9 +150,11 @@ const Admin = (props) => { const [selectedOrganization, setSelectedOrganization] = React.useState({}); //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([]); @@ -116,21 +170,30 @@ const Admin = (props) => { 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 [billingInfo, ] = React.useState({}); const [adminTab, setAdminTab] = React.useState(2); - const [showApiKey, setShowApiKey] = useState(false); - const [selectedStatus, setSelectedStatus] = React.useState([]); + const [showApiKey, setShowApiKey] = useState(false); + const [billingInfo, setBillingInfo] = React.useState({}); + const [selectedStatus, setSelectedStatus] = React.useState([]); useEffect(() => { - getUsers() + getUsers() }, []); + + useEffect(() => { + if (isDropzone) { + //redirectOpenApi(); + setIsDropzone(false); + } + }, [isDropzone]); + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const get2faCode = (userId) => { @@ -162,7 +225,39 @@ const Admin = (props) => { }); }; - + 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 = [ { @@ -191,19 +286,22 @@ const Admin = (props) => { ] */ - const alert = useAlert(); + const alert = useAlert(); const handleStatusChange = (event) => { const { value } = event.target; + console.log("value: ", value) setSelectedStatus(value); - handleEditOrg( + + + handleEditOrg( "", "", selectedOrganization.id, "", {}, {}, - value, + value.length === 0 ? ["none"] : value, ) } @@ -235,67 +333,66 @@ const Admin = (props) => { if (org.security_framework !== undefined && org.security_framework !== null) { if (org.security_framework.cases.name !== undefined && org.security_framework.cases.name !== null && org.security_framework.cases.name !== "") { - your_apps += org.security_framework.cases.name.replace("_", " ", -1) + ", " + your_apps += org.security_framework.cases.name.replace("_", " ", -1).replace(" API", "", -1) + ", " if (subject_add < 2) { if (subject_add === 1) { - subject += " & " + subject += " and " } subject_add += 1 - subject += org.security_framework.cases.name.replace("_", " ", -1) + subject += org.security_framework.cases.name.replace("_", " ", -1).replace(" API", "", -1) } } if (org.security_framework.siem.name !== undefined && org.security_framework.siem.name !== null && org.security_framework.siem.name !== "") { - your_apps += org.security_framework.siem.name.replace("_", " ", -1) + ", " - + your_apps += org.security_framework.siem.name.replace("_", " ", -1).replace(" API", "", -1) + ", " if (subject_add < 2) { if (subject_add === 1) { - subject += " & " + subject += " and " } subject_add += 1 - subject += org.security_framework.siem.name.replace("_", " ", -1) + subject += org.security_framework.siem.name.replace("_", " ", -1).replace(" API", "", -1) } } if (org.security_framework.communication.name !== undefined && org.security_framework.communication.name !== null && org.security_framework.communication.name !== "") { - your_apps += org.security_framework.communication.name.replace("_", " ", -1) + ", " + your_apps += org.security_framework.communication.name.replace("_", " ", -1).replace(" API", "", -1) + ", " if (subject_add < 2) { if (subject_add === 1) { - subject += " & " + subject += " and " } subject_add += 1 - subject += org.security_framework.communication.name.replace("_", " ", -1) + subject += org.security_framework.communication.name.replace("_", " ", -1).replace(" API", "", -1) } } if (org.security_framework.edr.name !== undefined && org.security_framework.edr.name !== null && org.security_framework.edr.name !== "") { - your_apps += org.security_framework.edr.name.replace("_", " ", -1) + ", " + your_apps += org.security_framework.edr.name.replace("_", " ", -1).replace(" API", "", -1) + ", " if (subject_add < 2) { if (subject_add === 1) { - subject += " & " + subject += " and " } subject_add += 1 - subject += org.security_framework.edr.name.replace("_", " ", -1) + subject += org.security_framework.edr.name.replace("_", " ", -1).replace(" API", "", -1) } } if (org.security_framework.intel.name !== undefined && org.security_framework.intel.name !== null && org.security_framework.intel.name !== "") { - your_apps += org.security_framework.intel.name.replace("_", " ", -1) + ", " + your_apps += org.security_framework.intel.name.replace("_", " ", -1).replace(" API", "", -1) + ", " if (subject_add < 2) { if (subject_add === 1) { - subject += " & " + subject += " and " } subject_add += 1 - subject += org.security_framework.intel.name.replace("_", " ", -1) + subject += org.security_framework.intel.name.replace("_", " ", -1).replace(" API", "", -1) } } @@ -363,7 +460,7 @@ ${usecases} Let me know if you're interested, or set up a call here: https://drift.me/${username}` - return `mailto:${admins}?bcc=frikky@shuffler.io&subject=${subject}&body=${body}` + return `mailto:${admins}?bcc=frikky@shuffler.io,binu@shuffler.io&subject=${subject}&body=${body}` } const deleteAuthentication = (data) => { @@ -578,6 +675,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user } else if (!responseJson.success) { alert.error("Failed to handle sync."); } else { + getOrgs(); if (disableSync) { alert.success("Successfully disabled sync!"); setOrgSyncResponse("Successfully disabled syncronization"); @@ -900,22 +998,26 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user leads.push("internal") } + if (responseJson.lead_info.sub_org) { + leads.push("sub_org") + } + setSelectedStatus(leads) } setSelectedOrganization(responseJson) - //var lists = { - // active: { - // triggers: [], - // features: [], - // sync: [], - // }, - // inactive: { - // triggers: [], - // features: [], - // sync: [], - // }, - //}; + var lists = { + active: { + triggers: [], + features: [], + sync: [], + }, + inactive: { + triggers: [], + features: [], + sync: [], + }, + }; // FIXME: Set up features //Object.keys(responseJson.sync_features).map(function(key, index) { @@ -924,7 +1026,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user //setOrgName(responseJson.name) //setOrgDescription(responseJson.description) - //setOrganizationFeatures(lists); + setOrganizationFeatures(lists); } }) .catch((error) => { @@ -1017,7 +1119,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user alert.info("Setting default env to " + environment.name); var newEnv = []; for (var key in environments) { - if (environments[key].id === environment.id) { + if (environments[key].id == environment.id) { if (environments[key].archived) { alert.error("Can't set archived to default"); return; @@ -1025,7 +1127,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user environments[key].default = true; } else if ( - environments[key].default === true && + environments[key].default == true && environments[key].id !== environment.id ) { environments[key].default = false; @@ -1065,6 +1167,33 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }); }; + 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 rerunCloudWorkflows = (environment) => { alert.info("Starting execution reruns. This can run in the background.") fetch( @@ -1154,7 +1283,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user //alert.info("Modifying environment " + environment.Name) var newEnv = []; for (var key in environments) { - if (environments[key].id === id) { + if (environments[key].id == id) { if (environments[key].default) { alert.error("Can't modify the default environment"); return; @@ -1233,6 +1362,9 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }); }; + var localData = ""; + + const getSchedules = () => { fetch(globalUrl + "/api/v1/workflows/schedules", { method: "GET", @@ -1314,6 +1446,34 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }); }; + 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", @@ -1406,8 +1566,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user document.title = "Shuffle - admin - environments"; getEnvironments(); } else if (newValue === 7) { - //getOrgs(); document.title = "Shuffle - admin - orgs"; + getOrgs(); } else { document.title = "Shuffle - admin"; } @@ -1423,6 +1583,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user if (firstRequest) { setFirstRequest(false); document.title = "Shuffle - admin"; + + getEnvironments(); if (!isCloud) { getUsers(); } else { @@ -1909,6 +2071,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user maxHeight: 200, maxWidth: 200, minWidth: 200, + maxWidth: 200, }} /> ) : ( @@ -2036,7 +2199,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user primary: "Workflows", secondary: "", active: true, - icon: , + icon: , }, { primary: "Apps", @@ -2188,25 +2351,40 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
) : (
+ {/* + + { + console.log("Should go to icon") + }} + > + + + + */} {userdata.support === true ? - - - + {/**/} + Status

Tags

- { style={{ backgroundColor: inputColor, borderRadius: 5 }} InputProps={{ style: { - color: "white", - minHeight: "50px", - marginLeft: "5px", - maxWidth: "95%", - fontSize: "1em", - borderRadius: 5, }, }} disabled={ diff --git a/frontend/src/views/GettingStarted.jsx b/frontend/src/views/GettingStarted.jsx index 9324e159..fb96e671 100644 --- a/frontend/src/views/GettingStarted.jsx +++ b/frontend/src/views/GettingStarted.jsx @@ -69,7 +69,7 @@ import Dropzone from "../components/Dropzone.jsx"; import { useNavigate, Link, useParams } from "react-router-dom"; import { useAlert } from "react-alert"; -import ChipInput from "material-ui-chip-input"; +import { MuiChipsInput } from "mui-chips-input"; import { v4 as uuidv4 } from "uuid"; const inputColor = "#383B40"; @@ -1930,7 +1930,7 @@ const GettingStarted = (props) => { margin="dense" fullWidth /> - {
- { }} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: 500, padding: 30, @@ -835,7 +827,7 @@ const Workflows = (props) => { }} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: 500, padding: 50, @@ -892,7 +884,7 @@ const Workflows = (props) => { }} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: 500, }, @@ -1253,7 +1245,7 @@ const Workflows = (props) => { width: "100%", height: "250px", color: "white", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, display: "flex", flexDirection: "column", }; @@ -1271,7 +1263,7 @@ const Workflows = (props) => { overflow: "hidden", width: "100%", color: "white", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, padding: "12px 12px 0px 15px", borderRadius: 5, display: "flex", @@ -1283,7 +1275,7 @@ const Workflows = (props) => { height: "auto", color: "white", margin: "10px", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, }; const workflowActionStyle = { @@ -1603,7 +1595,7 @@ const Workflows = (props) => { const innerColor = "rgba(255,255,255,0.3)"; const setupPaperStyle = { minHeight: paperAppStyle.minHeight, - maxWidth: "100%", + maxWidth: "100%", minWidth: paperAppStyle.width, color: innerColor, padding: paperAppStyle.padding, @@ -1710,7 +1702,7 @@ const Workflows = (props) => { }} > { event.stopPropagation() ReactDOM.unstable_batchedUpdates(() => { @@ -1734,7 +1726,7 @@ const Workflows = (props) => { {"Edit details"} { setSelectedWorkflow(data); setPublishModalOpen(true); @@ -1745,7 +1737,7 @@ const Workflows = (props) => { {"Publish Workflow"} { copyWorkflow(data); setOpen(false); @@ -1755,7 +1747,7 @@ const Workflows = (props) => { {"Duplicate Workflow"} - {/*= 0} style={{backgroundColor: inputColor, color: "white"}} onClick={() => { + {/*= 0} style={{backgroundColor: theme.palette.inputColor, color: "white"}} onClick={() => { //copyWorkflow(data) //setOpen(false) }} key={"duplicate"}> @@ -1763,7 +1755,7 @@ const Workflows = (props) => { {"Copy to Child Org"} */} { setExportModalOpen(true); @@ -1817,7 +1809,7 @@ const Workflows = (props) => { {"Export Workflow"} { setDeleteModalOpen(true); setSelectedWorkflowId(data.id); @@ -1909,7 +1901,7 @@ const Workflows = (props) => { } return ( -
+
{selectedCategory !== "" ? @@ -2704,7 +2696,7 @@ const Workflows = (props) => { }} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: isMobile ? "90%" : "800px", maxWidth: isMobile ? "90%" : "800px", @@ -2760,7 +2752,7 @@ const Workflows = (props) => { fullWidth />
- { {isMobile ? null :
- { {usecases.map((usecase, index) => { //console.log(usecase) const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length/usecase.list.length*100) : 0 - console.log("Usecase Matches: ", usecase.matches, ", Percent: ", percentDone) + //console.log("Usecase Matches: ", usecase.matches, ", Percent: ", percentDone) return ( { } return ( - - {returnData} - + + {/**/} + {returnData} + {/**/} + ); })}
@@ -3500,12 +3494,12 @@ const Workflows = (props) => { /> : null} -
+
{view === "grid" ? ( - + {/**/} - + {/**/} {filteredWorkflows.map((data, index) => { // Shouldn't be a part of this list @@ -3524,11 +3518,13 @@ const Workflows = (props) => { } return ( - + + {/**/} - + {/**/} + ) })} @@ -3613,7 +3609,7 @@ const Workflows = (props) => { onClose={() => {}} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: "800px", minHeight: "320px", @@ -3640,7 +3636,7 @@ const Workflows = (props) => { Repository (supported: github, gitlab, bitbucket) {
{
{ fullWidth /> { }} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: 560, minHeight: 415, From bb8b14be64a194f23ac0055075b3a5b1ed24fb01 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 15 Aug 2023 21:58:00 +0200 Subject: [PATCH 015/160] Fixed most of the textfield and button problems from mui 18. Next is autocomplete --- frontend/src/components/EditWorkflow.jsx | 93 ++++--- frontend/src/theme.jsx | 3 +- frontend/src/views/AngularWorkflow.jsx | 319 ++++++++--------------- frontend/src/views/Workflows.jsx | 2 +- 4 files changed, 158 insertions(+), 259 deletions(-) diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index f9aacdde..41bdffea 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -26,6 +26,7 @@ import { Typography, Zoom, CircularProgress, + Drawer, Dialog, DialogTitle, DialogActions, @@ -58,6 +59,7 @@ const EditWorkflow = (props) => { const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove + const [submitLoading, setSubmitLoading] = React.useState(false); const [showMoreClicked, setShowMoreClicked] = React.useState(false); const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) @@ -144,18 +146,17 @@ const EditWorkflow = (props) => { var total_count = 0 return ( - { setModalOpen(false); }} PaperProps={{ style: { - backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: isMobile ? "90%" : 550, maxWidth: isMobile ? "90%" : 550, - minHeight: 400, + minHeight: 400, //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, }, @@ -220,11 +221,11 @@ const EditWorkflow = (props) => { -
+
{ - setName(event.target.value) - }} + onChange={(event) => { + setName(event.target.value) + }} InputProps={{ style: { color: "white", @@ -261,7 +262,7 @@ const EditWorkflow = (props) => {
{ color="primary" fullWidth value={newWorkflowTags} + onChange={(chip) => { + console.log("Chip: ", chip) + //newWorkflowTags.push(chip); + setNewWorkflowTags(chip); + }} onAdd={(chip) => { newWorkflowTags.push(chip); setNewWorkflowTags(newWorkflowTags); @@ -486,41 +492,48 @@ const EditWorkflow = (props) => { -
+ ) } diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index e9da742d..ef91194b 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -17,8 +17,8 @@ const theme = createTheme(adaptV4Theme({ secondary: "rgba(255,255,255,0.7)", }, type: "dark", - surfaceColor: "#27292d", inputColor: "#383B40", + surfaceColor: "#27292d", platformColor: "#1c1c1d", backgroundColor: "#1a1a1a", borderRadius: 5, @@ -34,6 +34,7 @@ const theme = createTheme(adaptV4Theme({ borderRadius: 5, }, innerTextfieldStyle: { + // Removed since upgrading to mui 18 //color: "white", //minHeight: 50, //marginLeft: "5px", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index c14260dd..f5ccd244 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -22,6 +22,8 @@ import algoliasearch from 'algoliasearch/lite'; import { Zoom, + Fade, + Avatar, Popover, TextField, @@ -46,7 +48,6 @@ import { IconButton, Menu, Input, - Fade, FormGroup, FormControlLabel, Typography, @@ -221,9 +222,6 @@ export const triggers = [ ]; // is_valid: cloudSyncEnabled || isCloud ? true : false, -const surfaceColor = "#27292D"; -const inputColor = "#383B40"; - // http://apps.cytoscape.org/apps/yfileslayoutalgorithms cytoscape.use(edgehandles); //cytoscape.use(clipboard); @@ -5899,7 +5897,7 @@ const AngularWorkflow = (defaultprops) => { maxWidth: isMobile ? 50 : "100%", marginTop: "5px", color: "white", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, cursor: "pointer", display: "flex", }; @@ -5912,7 +5910,7 @@ const AngularWorkflow = (defaultprops) => { maxWidth: "100%", marginTop: "5px", color: "white", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, cursor: "pointer", display: "flex", }; @@ -6014,7 +6012,7 @@ const AngularWorkflow = (defaultprops) => { open={open} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, }, }} onClose={() => { @@ -6024,7 +6022,7 @@ const AngularWorkflow = (defaultprops) => { > { @@ -6050,7 +6048,7 @@ const AngularWorkflow = (defaultprops) => { { @@ -6259,7 +6257,7 @@ const AngularWorkflow = (defaultprops) => { - {isMobile ? null : Variables} + {isMobile ? null : Vars} } style={tabStyle} @@ -6992,10 +6990,6 @@ const AngularWorkflow = (defaultprops) => { style={{ backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, maxWidth: leftBarSize - 20, }} InputProps={{ style: { - color: "white", - fontSize: "1em", - height: 50, - margin: 0, }, startAdornment: ( @@ -7202,18 +7196,13 @@ const AngularWorkflow = (defaultprops) => {
@@ -7254,11 +7243,13 @@ const AngularWorkflow = (defaultprops) => { delay += 75 return ( runDelay ? - + + {/**/}
-
+ {/*
*/} + :
{extraMessage} @@ -7902,16 +7893,11 @@ const AngularWorkflow = (defaultprops) => { var datafield = ( { return ( { if (data.type === "Execution Argument") { @@ -8106,7 +8092,7 @@ const AngularWorkflow = (defaultprops) => { const menuItemStyle = { color: "white", - backgroundColor: inputColor, + backgroundColor: theme.palette.inputColor, }; const conditionsModal = ( @@ -8122,7 +8108,7 @@ const AngularWorkflow = (defaultprops) => { PaperProps={{ style: { pointerEvents: "auto", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: isMobile ? "90%" : 800, border: theme.palette.defaultBorder, @@ -8227,7 +8213,7 @@ const AngularWorkflow = (defaultprops) => { anchorEl={variableAnchorEl} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, }, }} onClose={() => { @@ -8460,7 +8446,7 @@ const AngularWorkflow = (defaultprops) => { maxWidth: "100%", marginTop: "5px", color: "white", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, cursor: "pointer", display: "flex", }; @@ -8571,7 +8557,7 @@ const AngularWorkflow = (defaultprops) => { open={open} PaperProps={{ style: { - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, }, }} onClose={() => { @@ -8580,7 +8566,7 @@ const AngularWorkflow = (defaultprops) => { }} > { duplicateCondition(index); }} @@ -8589,7 +8575,7 @@ const AngularWorkflow = (defaultprops) => { {"Duplicate"} { setOpen(false); deleteCondition(index); @@ -9158,7 +9144,7 @@ const AngularWorkflow = (defaultprops) => {
{ } InputProps={{ style: { - color: "white", - height: 50, - marginLeft: "5px", - maxWidth: "95%", - fontSize: "1em", }, endAdornment: @@ -11121,17 +11040,13 @@ const AngularWorkflow = (defaultprops) => {
{ }} InputProps={{ style: { - color: "white", - marginLeft: "5px", - maxWidth: "95%", - fontSize: "1em", }, }} fullWidth @@ -11178,17 +11093,13 @@ const AngularWorkflow = (defaultprops) => {
{ }} InputProps={{ style: { - color: "white", - marginLeft: "5px", - maxWidth: "95%", - fontSize: "1em", }, }} fullWidth @@ -11213,7 +11124,7 @@ const AngularWorkflow = (defaultprops) => {
{isCloud && workflow.triggers[selectedTriggerIndex].parameters.length > 4 ? {
Name
{ Environment: {
{
{ workflow.triggers[selectedTriggerIndex].parameters[2].value.includes("email") ? ( { ].parameters[2].value.includes("sms") ? ( {
Name
{ setUpdate(Math.random()); }} style={{ - backgroundColor: inputColor, + backgroundColor: theme.palette.inputColor, color: "white", height: 50, }} @@ -12029,7 +11920,7 @@ const AngularWorkflow = (defaultprops) => { return ( {data} @@ -12071,16 +11962,11 @@ const AngularWorkflow = (defaultprops) => {
{
{ id="execution_argument_input_field" style={theme.palette.textFieldStyle} disabled={workflow.public} - InputProps={{ - style: theme.palette.innerTextfieldStyle, - }} color="secondary" placeholder={"Execution Argument"} defaultValue={executionText} @@ -12986,11 +12864,12 @@ const AngularWorkflow = (defaultprops) => { {defaultReturn} : - + + {/**/}
{defaultReturn}
-
+
); //return null; @@ -13316,11 +13195,11 @@ const AngularWorkflow = (defaultprops) => { color: "white", marginBottom: 10, padding: 5, - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, cursor: "pointer", display: "flex", - minHeight: 40, - maxHeight: 40, + minHeight: 50, + maxHeight: 50, }; const parsedExecutionArgument = () => { @@ -13766,7 +13645,6 @@ const AngularWorkflow = (defaultprops) => { overflow: "auto", minWidth: isMobile ? "100%" : 420, maxWidth: isMobile ? "100%" : 420, - backgroundColor: "#1F2023", color: "white", fontSize: 18, zIndex: 10005, @@ -13864,7 +13742,8 @@ const AngularWorkflow = (defaultprops) => { } return ( - + + {/**/}
{
-
+
); })}
) : ( -
There are no executions yet, or they are not loaded.
+
+ + There are no executions yet, or they are not loaded. + + +
)}
) : ( -
+
{ }} />
-

Execution info

+

Details

{ style={{ backgroundColor: "rgba(255,255,255,0.6)", marginTop: 15, - marginBottom: 30, + marginBottom: 20, }} /> {executionData.results !== undefined && @@ -14241,14 +14136,14 @@ const AngularWorkflow = (defaultprops) => { executionData.status !== "FAILURE" && executionData.status !== "WAITING" && !(executionData.results === undefined || executionData.results === null || (executionData.results.length === 0 && executionData.status === "EXECUTING")) ? ( -
- { - console.log(environments, defaultEnvironmentIndex, nonskippedResults) - }} /> + @@ -14256,12 +14151,12 @@ const AngularWorkflow = (defaultprops) => {
{ - executionData.results === undefined || + executionData.results === undefined || executionData.results === null || (executionData.results.length === 0 && executionData.status === "EXECUTING") ? ( -
- +
+ {environments.length > 0 && defaultEnvironmentIndex < environments.length && nonskippedResults.length === 0 && environments[defaultEnvironmentIndex].Name !== "Cloud" ? No results yet. Is Orborus running for the "{environments[defaultEnvironmentIndex].Name}" environment? Learn more. If the Workflow doesn't start within 30 seconds with Orborus running, contact support: support@shuffler.io @@ -14755,7 +14650,7 @@ const AngularWorkflow = (defaultprops) => { PaperProps={{ style: { pointerEvents: "auto", - backgroundColor: inputColor, + backgroundColor: theme.palette.inputColor, color: "white", minWidth: isMobile ? "90%" : 650, padding: 30, @@ -15066,7 +14961,8 @@ const AngularWorkflow = (defaultprops) => {
) : ( - + + {/**/} { style={{ width: cytoscapeWidth, height: bodyHeight - appBarSize - 5, - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, }} stylesheet={cystyle} boxSelectionEnabled={true} @@ -15089,7 +14985,7 @@ const AngularWorkflow = (defaultprops) => { setCy(incy); }} /> - + )}
{executionModal} @@ -15165,7 +15061,7 @@ const AngularWorkflow = (defaultprops) => { PaperProps={{ style: { pointerEvents: "auto", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", border: theme.palette.defaultBorder, maxWidth: isMobile ? bodyWidth - 100 : 800, @@ -15343,7 +15239,7 @@ const AngularWorkflow = (defaultprops) => { PaperProps={{ style: { pointerEvents: "auto", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", border: theme.palette.defaultBorder, maxWidth: isMobile ? bodyWidth - 100 : "100%", @@ -15632,16 +15528,11 @@ const AngularWorkflow = (defaultprops) => { Name - what is this used for? { ) : ( { open={configureWorkflowModalOpen} PaperProps={{ style: { - backgroundColor: surfaceColor, color: "white", minWidth: 650, border: theme.palette.defaultBorder, @@ -15844,7 +15729,7 @@ const AngularWorkflow = (defaultprops) => { PaperProps={{ style: { pointerEvents: "auto", - backgroundColor: surfaceColor, + backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: 1100, minHeight: 700, @@ -15965,7 +15850,7 @@ const AngularWorkflow = (defaultprops) => {
{ style={{ marginTop: 25, marginBottom: 25, - backgroundColor: inputColor, + backgroundColor: theme.palette.inputColor, }} /> @@ -16721,7 +16606,7 @@ const AngularWorkflow = (defaultprops) => { : null} {/*selectionOpen === true ? -
+
{ overflow: "hidden", width: "100%", color: "white", - backgroundColor: theme.palette.surfaceColor, padding: "12px 12px 0px 15px", borderRadius: 5, display: "flex", boxSizing: "border-box", position: "relative", + backgroundColor: theme.palette.surfaceColor, }; const gridContainer = { From e331d06b8a0dfaaa88b10968a0e6173b65ca64c7 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 16 Aug 2023 00:07:02 +0200 Subject: [PATCH 016/160] Fixed Select and Autocomplete prompts to properly select and work with mui18 --- frontend/src/components/EditWorkflow.jsx | 2 +- frontend/src/components/ParsedAction.jsx | 270 +++++++++++++---------- frontend/src/views/AngularWorkflow.jsx | 114 ++++++---- frontend/src/views/AppCreator.jsx | 8 + frontend/src/views/Workflows.jsx | 3 + 5 files changed, 234 insertions(+), 163 deletions(-) diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 41bdffea..c862f22b 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -475,7 +475,7 @@ const EditWorkflow = (props) => {
- + -
- ); - - // Requires src or dst as input - // FIXME - use this shit to edit an app or something - //const editButtonFix = (app, apptype) => { - // if (apptype === "src") { - - // } else if (apptype === "dst") { - - // } - //} - - const editSrcApp = (event) => { - // if tmpsrcapp, show RESET (can be an X or something too) - // if reset is clicked, set source app back to the original - setTmpSrcApp(scheduleConfig.appinfo.sourceapp); - - var tmpscheduleConfig = scheduleConfig; - tmpscheduleConfig.appinfo.sourceapp = {}; - - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(tmpscheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - setScheduleConfig({}); - setSrcApp({}); - setSrcAppAction(""); - }) - .catch((error) => { - console.log(error); - }); - }; - - const editDstApp = (event) => { - // if tmpsrcapp, show RESET (can be an X or something too) - // if reset is clicked, set source app back to the original - setTmpDstApp(scheduleConfig.appinfo.destinationapp); - - var tmpscheduleConfig = scheduleConfig; - tmpscheduleConfig.appinfo.destinationapp = {}; - - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(tmpscheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - setScheduleConfig({}); - setDstApp({}); - setDstAppAction(""); - }) - .catch((error) => { - console.log(error); - }); - }; - - const scheduleApp = (app, actiondata) => { - const editButton = - actiondata === "src" ? ( - - ) : ( - - ); - - const configureButton = - actiondata === "src" ? ( - - ) : ( - - ); - - // FIXME - set src vs dst - return ( - - - - - - - {splitter} - - - -
-

{app.name}

-
-
{app.description}
-
- {app.action} -
- {splitter} - -
{editButton}
-
{configureButton}
-
-
-
- ); - }; - - const splitter = ( -
- ); - - const dstDownshift = ( - - {({ - getInputProps, - getItemProps, - getMenuProps, - highlightedIndex, - inputValue, - isOpen, - selectedItem, - }) => ( -
- {renderInput("input", { - fullWidth: true, - InputProps: getInputProps({ - placeholder: "Search destination apps", - }), - })} - -
-
- {isOpen ? ( - - {getSuggestions(inputValue, "input").map( - (suggestion, index) => - renderSuggestion({ - suggestion, - index, - itemProps: getItemProps({ item: suggestion.name }), - highlightedIndex, - selectedItem, - }) - )} - - ) : null} -
- {dstappaction} -
-
- )} -
- ); - - const srcDownshift = ( - - {({ - getInputProps, - getItemProps, - getMenuProps, - highlightedIndex, - inputValue, - isOpen, - selectedItem, - }) => ( -
- {renderInput("output", { - fullWidth: true, - InputProps: getInputProps({ - placeholder: "Search source apps", - }), - })} - -
-
- {isOpen ? ( - - {getSuggestions(inputValue, "output").map( - (suggestion, index) => - renderSuggestion({ - suggestion, - index, - itemProps: getItemProps({ item: suggestion.name }), - highlightedIndex, - selectedItem, - }) - )} - - ) : null} -
- {srcappaction} -
-
- )} -
- ); - - const submitDstApp = (event) => { - var packagedSrc = { - name: dstApp.name, - id: dstApp.id, - description: dstApp.description, - action: dstAppAction, - }; - - var tmpscheduleConfig = scheduleConfig; - - tmpscheduleConfig.appinfo.destinationapp = packagedSrc; - - // Hmm, do this here? Idk - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(tmpscheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - setScheduleConfig({}); - }) - .catch((error) => { - console.log(error); - }); - }; - - const submitSrcApp = (event) => { - var packagedSrc = { - name: srcApp.name, - id: srcApp.id, - description: srcApp.description, - action: srcAppAction, - }; - - var tmpscheduleConfig = scheduleConfig; - - // FIXME - future fred - // - tmpscheduleConfig.appinfo.sourceapp = packagedSrc; - - // Hmm, do this here? Idk - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(tmpscheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - setScheduleConfig({}); - }) - .catch((error) => { - console.log(error); - }); - }; - - const resetDstApp = (event) => { - var tmpscheduleConfig = scheduleConfig; - tmpscheduleConfig.appinfo.destinationapp = tmpDstApp; - - // Hmm, do this here? Idk - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(tmpscheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - setScheduleConfig({}); - setDstApp(tmpSrcApp); - setDstAppAction(tmpDstApp.action); - setTmpDstApp({}); - }) - .catch((error) => { - console.log(error); - }); - }; - - const resetSrcApp = (event) => { - var tmpscheduleConfig = scheduleConfig; - tmpscheduleConfig.appinfo.sourceapp = tmpSrcApp; - - // Hmm, do this here? Idk - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(tmpscheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - setScheduleConfig({}); - setSrcApp(tmpSrcApp); - setSrcAppAction(tmpSrcApp.action); - setTmpSrcApp({}); - }) - .catch((error) => { - console.log(error); - }); - }; - - // Based on some srcthing - const searchGridSrc = ( - - - Choose Source app (FIXME) - clickable - - {splitter} - - - -
{srcDownshift}
-
-
- {splitter} - -
- -
-
- -
-
-
-
- ); - - const searchGridDst = ( - - - Choose Source app (FIXME) - clickable - - {splitter} - - - -
{dstDownshift}
-
-
- {splitter} - -
- -
-
- -
-
-
-
- ); - - const srcField = - Object.getOwnPropertyNames(scheduleConfig).length > 0 && - Object.getOwnPropertyNames(scheduleConfig.appinfo.sourceapp).length > 0 && - scheduleConfig.appinfo.sourceapp.name.length > 0 ? ( -
- - {scheduleApp(scheduleConfig.appinfo.sourceapp, "src")} - -
- ) : ( -
- - {searchGridSrc} - -
- ); - - const dstField = - Object.getOwnPropertyNames(scheduleConfig).length > 0 && - Object.getOwnPropertyNames(scheduleConfig.appinfo.destinationapp).length > - 0 && - scheduleConfig.appinfo.destinationapp.name.length > 0 ? ( -
- - {scheduleApp(scheduleConfig.appinfo.destinationapp, "dst")} - -
- ) : ( -
- - {searchGridDst} - -
- ); - - // Have to use array cus of datastore lol (no map[string]string) - const srcModalData = []; - const buildSrcModal = (event, fieldname) => { - var fieldfound = false; - for (var key in srcModalData) { - if (srcModalData[key]["key"] === fieldname) { - fieldfound = true; - srcModalData[key]["value"] = event.target.value; - break; - } - } - - if (!fieldfound) { - srcModalData.push({ key: fieldname, value: event.target.value }); - } - console.log(srcModalData); - }; - - // FIXME - verify required fields? - const submitSrcConfig = () => { - scheduleConfig.appinfo.sourceapp.config = srcModalData; - setScheduleConfig(scheduleConfig); - setSrcAppConfigOpen(false); - - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(scheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - console.log(responseJson); - }) - .catch((error) => { - console.log(error); - }); - }; - - const dstModalData = []; - const buildDstModal = (event, fieldname) => { - var fieldfound = false; - for (var key in dstModalData) { - if (dstModalData[key]["key"] === fieldname) { - fieldfound = true; - dstModalData[key]["value"] = event.target.value; - break; - } - } - - if (!fieldfound) { - dstModalData.push({ key: fieldname, value: event.target.value }); - } - console.log(dstModalData); - }; - - // FIXME - verify required fields - const submitDstConfig = () => { - scheduleConfig.appinfo.destinationapp.config = dstModalData; - setScheduleConfig(scheduleConfig); - setDstAppConfigOpen(false); - - fetch(globalUrl + "/api/v1/schedules/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(scheduleConfig), - }) - .then((response) => response.json()) - .then((responseJson) => { - console.log(responseJson); - }) - .catch((error) => { - console.log(error); - }); - }; - - const dstConfigModal = dstAppConfigOpen ? ( - { - setDstAppConfigOpen(false); - }} - > - Source configuration - - Configure {dstApp.name}'s required fields - {dstAppConfig.map((data) => ( - { - buildDstModal(event, data.name); - }} - autofocus - color="primary" - name="searchtext" - placeholder={data.name} - margin="dense" - id={data.name} - label={data.name} - fullWidth - /> - ))} - - - - - - - ) : null; - - // FIXME - load the actual fields! - const srcConfigModal = srcAppConfigOpen ? ( - { - setSrcAppConfigOpen(false); - }} - > - Source configuration - - Configure {srcApp.name}'s required fields - {srcAppConfig.map((data) => ( - { - buildSrcModal(event, data.name); - }} - autofocus - color="primary" - name="searchtext" - placeholder={data.name} - margin="dense" - id={data.name} - label={data.name} - fullWidth - /> - ))} - - - - - - - ) : null; - - return ( -
- {srcConfigModal} - {dstConfigModal} -
- {srcField} - {dstField} -
- -
{submitButton}
-
{relationshipBody}
-
- ); -}; - -export default EditSchedule; diff --git a/frontend/src/views/EditWebhook.jsx b/frontend/src/views/EditWebhook.jsx deleted file mode 100755 index e1a5d516..00000000 --- a/frontend/src/views/EditWebhook.jsx +++ /dev/null @@ -1,393 +0,0 @@ -import React, { useState, useEffect } from "react"; - -import Button from "@material-ui/core/Button"; -import Paper from "@material-ui/core/Paper"; -import Divider from "@material-ui/core/Divider"; -import Select from "@material-ui/core/Select"; -import MenuItem from "@material-ui/core/MenuItem"; - -import WebhookImage from "../assets/img/webhook.png"; -import KafkaImage from "../assets/img/kafka.png"; - -import EditWorkflow from "./EditWorkflow"; - -const EditWebhook = (props) => { - const { globalUrl, isLoaded } = props; - - // FIXME - //const [webhookData, setWebhookData] = useState(webhooktest) - const [webhookData, setWebhookData] = useState({}); - const [workflows, setWorkflows] = useState([]); - const [firstrequest, setFirstrequest] = React.useState(true); - - const [selectedWorkflows, setSelectedWorkflows] = useState([]); - - const getWorkflows = () => { - fetch(globalUrl + "/api/v1/workflows", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for workflows :O!"); - } - return response.json(); - }) - .then((responseJson) => { - setWorkflows(responseJson); - }) - .catch((error) => { - console.log(error); - }); - }; - - const setWebhook = (inputdata) => { - console.log(inputdata); - - fetch(globalUrl + "/api/v1/hooks/" + props.match.params.key, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - body: JSON.stringify(inputdata), - }) - .then((response) => response.json()) - .then((responseJson) => { - console.log(responseJson); - }) - .catch((error) => { - console.log(error); - }); - }; - - const getCurrentWebhook = () => { - fetch(globalUrl + "/api/v1/hooks/" + props.match.params.key, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200!"); - window.location.pathname = "webhooks"; - } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.actions === null) { - responseJson.actions = []; - } - - if (responseJson.transforms === null) { - responseJson.transforms = []; - } - - setWebhookData(responseJson); - }) - .catch((error) => { - console.log(error); - //window.location.pathname = "webhooks" - }); - }; - - useEffect(() => { - if (firstrequest) { - setFirstrequest(false); - getCurrentWebhook(); - if (workflows.length <= 0) { - getWorkflows(); - } - } - - // After everything is loaded - if ( - Object.getOwnPropertyNames(webhookData).length > 0 && - webhookData.actions.length > 0 && - workflows.length > 0 && - selectedWorkflows.length === 0 - ) { - // Setting startup actions. making like this in case we want other actions - var tmpActionWorkflows = []; - for (var key in webhookData.actions) { - if (webhookData.actions[key].type === "workflow") { - tmpActionWorkflows.push(webhookData.actions[key]); - } - } - - // Fix duplicates... Meh - var foundWorkflowIds = []; - var tmpWorkflows = []; - for (key in tmpActionWorkflows) { - if (foundWorkflowIds.includes(tmpActionWorkflows[key].id)) { - continue; - } - - for (var subkey in workflows) { - if (tmpActionWorkflows[key].id === workflows[subkey]["id_"]) { - console.log(tmpActionWorkflows[key].id, workflows[subkey]["id_"]); - foundWorkflowIds.push(tmpActionWorkflows[key].id); - tmpWorkflows.push(workflows[subkey]); - break; - } - } - } - - if (tmpWorkflows.length > 0) { - setSelectedWorkflows(tmpWorkflows); - } - } - }); - - const hookPicture = - Object.getOwnPropertyNames(webhookData).length > 0 && - webhookData.type === "webhook" ? ( - webhook - ) : ( - MQ - ); - - const executeHook = (action) => { - fetch( - globalUrl + "/api/v1/hooks/" + props.match.params.key + "/" + action, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - } - ) - .then((response) => response.json()) - .then((responseJson) => { - setWebhookData({}); - }) - .catch((error) => { - console.log(error); - }); - }; - - const headerPaperStyle = { - display: "flex", - maxHeight: "800px", - minHeight: "800px", - margin: "10px 30px 10px 10px", - padding: "10px 5px 5px 5px", - flexDirection: "column", - }; - - // FIXME - add with counter to change the correct one (not just edit) - const addNewWorkflow = (event) => { - // Verify if it already exists in the array. Returns if it exists - for (var key in selectedWorkflows) { - var item = selectedWorkflows[key]; - if (item["id_"] === event.target.value["id_"]) { - return; - } - } - - // FIXME - make this possible for all accounts - if (selectedWorkflows.length === 0) { - console.log("ADD FIRST ITEM FOR SELECTEDWORKFLOWS"); - console.log(event.target.value); - - // Cleanup previous actions - var newActions = []; - if (webhookData.actions.length > 0) { - for (key in webhookData.actions) { - if ( - webhookData.actions[key].type === "" || - webhookData.actions[key].type === undefined - ) { - continue; - } - - newActions.push(webhookData.actions[key]); - } - } - - // FIXME - how to stringify this better hurr - var formattedWorkflow = { - type: "workflow", - name: event.target.value.name, - id: event.target.value.id_, - field: "", - }; - - // FIXME: patch this n - newActions.push(formattedWorkflow); - console.log(newActions); - - webhookData.actions = newActions; - setWebhook(webhookData); - } - - var tmpSelectedWorkflows = [].concat(selectedWorkflows, [ - event.target.value, - ]); - setSelectedWorkflows(tmpSelectedWorkflows); - }; - - // FIXME - // Create a list with + button - // For each, choose the new workflow I wanna add - // Current: JUST ONE - const selectedWorkflowIds = selectedWorkflows.map((data) => { - return data["id_"]; - }); - const availableWorkflows = workflows.filter( - (data) => !selectedWorkflowIds.includes(data["id_"]) - ); - - const WorkflowSelect = (counter) => { - if (selectedWorkflows[counter.counter] === undefined) { - return null; - } - - console.log(selectedWorkflows[0]); - console.log(selectedWorkflows[0]); - console.log(selectedWorkflows[0]); - console.log(selectedWorkflows[counter.counter]); - console.log(selectedWorkflows[counter.counter].name); - return ( -
- Workflow select: - -
- ); - }; - - const extraWorkflow = - workflows.length > 0 && availableWorkflows.length > 0 ? ( - - ) : null; - - const multiWorkflowSelect = - workflows.length > 0 && selectedWorkflows.length > 0 ? ( -
- {selectedWorkflows.map((data, count) => ( - - ))} - {extraWorkflow} -
- ) : ( - - ); - - const headerInfo = - Object.getOwnPropertyNames(webhookData).length > 0 ? ( -
- -
-
{hookPicture}
-
-
-

Name: {webhookData.info.name}

-
-
-
-
- Description: {webhookData.info.description} -
Id: {webhookData.id}
-
Url: {webhookData.info.url}
-
Type: {webhookData.type}
-
Status: {webhookData.status}
-
- CHOOSE ACTIONS: - {multiWorkflowSelect} -
-
- -
-
- -
-
- -
-
-
-
- ) : null; - - // FIXME - needs refresh every time you add a new workflow - const workflowdata = - Object.getOwnPropertyNames(webhookData).length > 0 && - selectedWorkflows.length > 0 ? ( - - ) : null; - - const loadedCheck = isLoaded ? ( -
-
{workflowdata}
-
{headerInfo}
-
- ) : ( -
- ); - - // FIXME: Use this for testing - // : null - return
{loadedCheck}
; -}; - -export default EditWebhook; diff --git a/frontend/src/views/ForgotPassword.jsx b/frontend/src/views/ForgotPassword.jsx deleted file mode 100755 index e4c95013..00000000 --- a/frontend/src/views/ForgotPassword.jsx +++ /dev/null @@ -1,118 +0,0 @@ -/* eslint-disable react/no-multi-comp */ -import React, { useState } from "react"; - -import TextField from "@material-ui/core/TextField"; -import Button from "@material-ui/core/Button"; -import Paper from "@material-ui/core/Paper"; - -const bodyDivStyle = { - margin: "auto", - marginTop: "100px", - width: "500px", -}; - -const ForgotPassword = (props) => { - const { globalUrl, isLoaded, isLoggedIn, surfaceColor, inputColor } = props; - - const boxStyle = { - paddingLeft: "30px", - paddingRight: "30px", - paddingBottom: "30px", - paddingTop: "30px", - backgroundColor: surfaceColor, - }; - - const [username, setUsername] = useState(""); - const [resetInfo, setResetInfo] = useState( - "You will receive an email with instructions shortly." - ); - - const handleValidateForm = () => { - return username.length > 3; - }; - - if (isLoggedIn === true) { - window.location.pathname = "/"; - } - - const onSubmit = (e) => { - e.preventDefault(); - // FIXME - add some check here ROFL - - // Just use this one? - var data = { username: username }; - var baseurl = globalUrl; - var url = baseurl + "/api/v1/passwordresetmail"; - fetch(url, { - method: "POST", - body: JSON.stringify(data), - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - }) - .then((response) => - response.json().then((responseJson) => { - if (responseJson["success"] === false) { - setResetInfo(responseJson["reason"]); - } - }) - ) - .catch((error) => { - setResetInfo("Error in userdata: " + error); - }); - }; - - const onChangeUser = (e) => { - setUsername(e.target.value); - }; - - const data = ( -
- -
-

Password reset

-
- -
-
- -
-
{resetInfo}
-
-
-
- ); - - const loadedCheck = isLoaded ?
{data}
:
; - - return
{loadedCheck}
; -}; - -export default ForgotPassword; diff --git a/frontend/src/views/ForgotPasswordLink.jsx b/frontend/src/views/ForgotPasswordLink.jsx deleted file mode 100755 index 8944deed..00000000 --- a/frontend/src/views/ForgotPasswordLink.jsx +++ /dev/null @@ -1,136 +0,0 @@ -import React, { useState, useEffect } from "react"; - -import Paper from "@material-ui/core/Paper"; -import Button from "@material-ui/core/Button"; - -import TextField from "@material-ui/core/TextField"; - -const bodyDivStyle = { - margin: "auto", - textAlign: "center", - width: "768px", -}; - -const boxStyle = { - flex: "1", - marginLeft: "10px", - marginRight: "10px", - paddingLeft: "30px", - paddingRight: "30px", - paddingBottom: "30px", - paddingTop: "30px", - backgroundColor: "#e8eaf6", - display: "flex", - flexDirection: "column", -}; - -//const tmpdata = { -// "username": "frikky", -// "firstname": "fred", -// "lastname": "ode", -// "title": "topkek", -// "companyname": "company here", -// "email": "your email pls", -// "phone": "PHONE!!", -//} - -// FIXME - add fetch for data fields -// FIXME - remove tmpdata -// FIXME: Use isLoggedIn :) -const Settings = (props) => { - const { globalUrl, isLoaded } = props; - - const [newPassword, setNewPassword] = useState(""); - const [newPassword2, setNewPassword2] = useState(""); - const [passwordFormMessage, setPasswordFormMessage] = useState(""); - - const onPasswordChange = () => { - const data = { - newpassword: newPassword, - newpassword2: newPassword2, - reference: props.match.params.key, - }; - const url = globalUrl + "/api/v1/passwordreset"; - 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) { - setPasswordFormMessage(responseJson["reason"]); - } - }) - ) - .catch((error) => { - setPasswordFormMessage("Something went wrong."); - }); - }; - - // This should "always" have data - useEffect(() => {}); - - // Random names for type & autoComplete. Didn't research :^) - const landingpageData = ( -
- -

Password Reset

-
- setNewPassword(e.target.value)} - /> - setNewPassword2(e.target.value)} - /> -
- -

{passwordFormMessage}

-
-
- ); - - const loadedCheck = isLoaded ? ( -
{landingpageData}
- ) : ( -
- ); - - return
{loadedCheck}
; -}; -export default Settings; diff --git a/frontend/src/views/GettingStarted.jsx b/frontend/src/views/GettingStarted.jsx index fb96e671..caba5b97 100644 --- a/frontend/src/views/GettingStarted.jsx +++ b/frontend/src/views/GettingStarted.jsx @@ -58,9 +58,7 @@ import { CloudDownload as CloudDownloadIcon, } from "@mui/icons-material"; -//import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; -//https://next.material-ui.com/components/material-icons/ import { DataGrid, GridToolbar } from "@mui/x-data-grid"; //import JSONPretty from 'react-json-pretty'; diff --git a/frontend/src/views/Introduction.jsx b/frontend/src/views/Introduction.jsx deleted file mode 100755 index e94295f3..00000000 --- a/frontend/src/views/Introduction.jsx +++ /dev/null @@ -1,221 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { Link, useParams } from "react-router-dom"; -import theme from '../theme.jsx'; - -import Grid from "@material-ui/core/Grid"; -import Card from "@material-ui/core/Card"; -import CardActionArea from "@material-ui/core/CardActionArea"; -import CardContent from "@material-ui/core/CardContent"; -import CardHeader from "@material-ui/core/CardHeader"; -import Typography from "@material-ui/core/Typography"; -import Button from "@material-ui/core/Button"; - -const Workflows = (defaultprops) => { - const { globalUrl, isLoggedIn, isLoaded } = defaultprops; - - const params = useParams(); - var props = JSON.parse(JSON.stringify(defaultprops)) - props.match = {} - props.match.params = params - - const [curView, setCurView] = useState(0); - const [firstrequest, setFirstrequest] = useState(true); - const [selectedItems, setSelectedItems] = useState([]); - - const viewdata1 = [ - { - title: "General", - content: "Learn about our ticketing solutions", - subitems: [ - { - name: "Search", - subtitle: "Search for anything, anywhere", - }, - { - name: "Message", - subtitle: "Read and send messages", - }, - { - name: "Parse emails", - subtitle: "what", - }, - ], - }, - { - title: "Ticketing", - subitems: [ - { - name: "Search", - subtitle: "Search for anything, anywhere", - }, - { - name: "Message", - subtitle: "Read and send messages", - }, - { - name: "Parse emails", - subtitle: "what", - }, - ], - }, - { - title: "Threat intel", - subitems: [ - { - name: "Search", - subtitle: "Search for anything, anywhere", - }, - { - name: "Message", - subtitle: "Read and send messages", - }, - { - name: "Parse emails", - subtitle: "what", - }, - ], - }, - ]; - - if (firstrequest) { - setFirstrequest(false); - if (props.match.params.key) { - console.log("PROPS: ", props.match.params.key); - const viewitem = viewdata1.find( - (item) => - item.title.toLowerCase() === props.match.params.key.toLowerCase() - ); - if (viewitem !== undefined && viewitem !== null) { - setCurView(1); - //setSelectedItem(viewitem) - } - } - } - - const cardContentStyle = { - height: "100%", - width: "100%", - padding: 40, - }; - - const outerGridView = { - width: "100%", - marginTop: 15, - }; - - const paperStyle = { - height: 300, - color: "white", - backgroundColor: theme.palette.surfaceColor, - color: "white", - cursor: "pointer", - display: "flex", - textAlign: "center", - }; - - const HandleSelection = (data) => { - const [selected, setSelected] = useState(false); - - var baseStyle = JSON.parse(JSON.stringify(paperStyle)); - if (selected) { - baseStyle.backgroundColor = "white"; - baseStyle.color = "black"; - } - - return ( - { - console.log(selectedItems); - if (selected) { - const index = selectedItems.findIndex( - (item) => item.title === data.title - ); - if (index >= 0) { - selectedItems.splice(index, 1); - setSelectedItems(selectedItems); - } - } else { - selectedItems.push(data); - setSelectedItems(selectedItems); - } - - setSelected(!selected); - - //setCurView(1) - //setSelectedItem(data) - //window.location.pathname += "/"+data.title.toLowerCase() - }} - > - - - - {data.title} - - - - - ); - }; - - const view1 = - curView === 0 ? ( -
- What are you interested in? - - {viewdata1.map((data) => { - return HandleSelection(data); - })} - - {/* - - */} -
- ) : null; - - const view2 = - curView === 1 ? ( -
- Step 2. - {/* - - {selectedItem.subitems === undefined ? null : - selectedItem.subitems.map(data => { - return ( - - - - - - {data.name} - - - {data.subtitle} - - - - - - ) - })} - - */} -
- ) : null; - - const baseView = ( -
- {view1} - {view2} -
- ); - - return
{baseView}
; -}; - -export default Workflows; diff --git a/frontend/src/views/Landingpage.jsx b/frontend/src/views/Landingpage.jsx deleted file mode 100755 index a0de1eee..00000000 --- a/frontend/src/views/Landingpage.jsx +++ /dev/null @@ -1,207 +0,0 @@ -import React from "react"; - -import Paper from "@material-ui/core/Paper"; -import Button from "@material-ui/core/Button"; -import Divider from "@material-ui/core/Divider"; -import { BrowserView, MobileView } from "react-device-detect"; -import { - Schedule as ScheduleIcon, - Web as WebIcon, - AccountTree as AccountTreeIcon, -} from "@mui/icons-material"; - -const bodyDivStyle = { - margin: "auto", - marginTop: "75px", - textAlign: "center", - width: "1100px", -}; - -const surfaceColor = "#27292D"; -const boxStyle = { - flex: "1", - marginLeft: "10px", - marginRight: "10px", - height: "400px", - //backgroundColor: "#e8eaf6", - backgroundColor: surfaceColor, - textAlign: "center", - display: "flex", - flexDirection: "column", -}; - -const bodyTextStyle = { - color: "#ffffff", -}; - -const hrefStyle = { - color: "black", - textDecoration: "none", -}; - -// Should be different if logged in :| -const LandingPage = (props) => { - const { isLoaded } = props; - - const textColor = "#8899A6"; - const iconColor = "#1DA1F2"; - const iconSize = "8em"; - const GridLayout = (header, description, link, icon) => { - return ( - - -
-

{header}

-
- -
- {description} -
-
{icon}
- -
-
Learn more
-
-
-
- ); - }; - - const listitems = [ - GridLayout( - "Simple integrations", - "Easily use others' or create your own integration", - "/docs/apps", - - ), - GridLayout( - "Workflows", - "Access the power of automation within minutes, whether its on premise or in the cloud", - "/docs/workflows", - - ), - GridLayout( - "Realtime actions", - "Beat the clock by leveraging our realtime triggers", - "/docs/triggers", - - ), - ]; - - // The actual landing page - // {"logo"} - const landingpageDataBrowser = ( -
-
-

Shuffle

-

- A general automation solution for Infosec and IT Professionals -

-
- - - - - - -
- {listitems.map((item) => { - return
{item}
; - })} -
-
- ); - - const landingpageDataMobile = ( -
-
-

Shuffle

-

A general automation solution for Infosec and IT Professionals

- - - -
-
-
{listitems[0]}
-
{listitems[1]}
-
- {listitems[2]} -
- -
-
- ); - - // Reroute if the user is logged in - // const landingSite = isLoggedIn ? :
{landingpageData}
- const landingSite =
{landingpageDataBrowser}
; - - const loadedCheck = isLoaded ? ( -
- {landingSite} - {landingpageDataMobile} -
- ) : ( -
- ); - - return
{loadedCheck}
; -}; -export default LandingPage; diff --git a/frontend/src/views/LandingpageNew.jsx b/frontend/src/views/LandingpageNew.jsx deleted file mode 100755 index c859beee..00000000 --- a/frontend/src/views/LandingpageNew.jsx +++ /dev/null @@ -1,531 +0,0 @@ -import React, { useState } from "react"; - -import Paper from "@material-ui/core/Paper"; -import Card from "@material-ui/core/Card"; -import CardActionArea from "@material-ui/core/CardActionArea"; -import CardMedia from "@material-ui/core/CardMedia"; -import CardContent from "@material-ui/core/CardContent"; -import CardActions from "@material-ui/core/CardActions"; -import Button from "@material-ui/core/Button"; -import Divider from "@material-ui/core/Divider"; -import Grid from "@material-ui/core/Grid"; -import { BrowserView, MobileView } from "react-device-detect"; - -import { - Schedule as ScheduleIcon, - Web as WebIcon, - AccountTree as AccountTreeIcon, - Info as InfoIcon, - ArrowForward as ArrowForwardIcon, - Create as CreateIcon, -} from "@mui/icons-material"; - -const bodyDivStyle = { - margin: "auto", -}; - -const surfaceColor = "#27292D"; -const boxStyle = { - flex: "1", - marginLeft: "10px", - marginRight: "10px", - height: "400px", - //backgroundColor: "#e8eaf6", - backgroundColor: surfaceColor, - textAlign: "center", - display: "flex", - flexDirection: "column", -}; - -const bodyTextStyle = { - color: "#ffffff", -}; - -const hrefStyle = { - color: "inherit", - textDecoration: "none", -}; - -// Should be different if logged in :| -const LandingPage = (props) => { - const { isLoaded } = props; - - const textColor = "#8899A6"; - const iconColor = "#1DA1F2"; - const iconSize = "8em"; - - const GridLayout = (header, description, link, icon) => { - return ( - - -
-

{header}

-
- -
- {description} -
-
{icon}
- -
-
Learn more
-
-
-
- ); - }; - - const listitems = [ - GridLayout( - "Simple integrations", - "Easily use others' or create your own integration", - "/docs/features", - - ), - GridLayout( - "Workflows", - "Access the power of automation within minutes, whether its on premise or in the cloud", - "/docs/features", - - ), - GridLayout( - "Realtime actions", - "Beat the clock by leveraging our realtime triggers", - "/docs/features", - - ), - ]; - - // The actual landing page - // {"logo"} - //We start by understanding your unique environment to help identify the right thing to automate. - const secondaryColor = "rgba(167,46,87,1)"; - const primaryColor = "rgba(25, 35, 94, 1)"; - - const paperStyle = { - flex: 1, - backgroundColor: "inherit", - cursor: "pointer", - }; - - const secondaryItemList = [ - { - primaryText: "No time to waste", - secondaryText: - "Bring all your applications into a single view, and make them all work together flawlessly!", - image: "/images/time.jpg", - }, - { - primaryText: "Get a better overview", - secondaryText: - "Don't know what's happening? We'll help you track and act on your most valuable KPI's!", - image: "/images/overview.jpg", - }, - { - primaryText: "Conquer your tasks", - secondaryText: - "Get access to powerful tools and pre-made workflows to help you crush your teams daily tasks!", - image: "/images/burnout.jpg", - }, - ]; - const [image, setImage] = useState(secondaryItemList[0].image); - - const landingpageDataBrowser = ( -
-
- -
-
Shuffle
-
- INFORMATION
OVERLOAD
-
-
- Everyone run into the same fundamental operational problems. Mailbox - chaos, tickets getting out of hand and a constant feeling of being - overwhelmed. The good news?{" "} -
- Shuffle solves them. -
-
- - - -
-
-
-
-
- Automation is just the beginning -
-
-
- {secondaryItemList.map((data, index) => { - const color = - image === data.image - ? "rgba(255,255,255,1)" - : "rgba(255,255,255,0.4)"; - return ( -
setImage(data.image)} - > - {data.primaryText} -
- {data.secondaryText} -
-
- ); - })} -
-
-
- -
-
-
-
- -
-
- Learn more about the benefits of Shuffle -
- -
-
-
-
-
- Focus on the work that matters to you -
-
- Menial tasks, scattered content, constant copy pasting, waste of - talent - there's a smarter way to work. -
-
- { - window.location.pathname = "/docs/features"; - }} - style={{ flex: 1, margin: 10, textAlign: "center" }} - > - - - -

Premade playbooks

-

Get your automation done with minimal effort

-
-
- -
- { - window.location.pathname = "/docs/features"; - }} - style={{ flex: 1, margin: 10, textAlign: "center" }} - > - - - -

Open frameworks

-

Mitre Att&ck, OpenAPI and more!

-
-
- -
- { - window.location.pathname = "/docs/features"; - }} - style={{ flex: 1, margin: 10, textAlign: "center" }} - > - - - -

Hundreds of integrations

-

Quickly integrate your software applications

-
-
- -
- { - window.location.pathname = "/docs/features"; - }} - > - - - -

Automated compliance

-

Stuck with compliance needs you can't meet?

-
-
- -
-
-
-
- ); - - const landingpageDataMobile = ( -
-
-

Shuffle

-

A general automation solution for Infosec and IT Professionals

- - - -
-
-
{listitems[0]}
-
{listitems[1]}
-
- {listitems[2]} -
- -
-
- ); - - // Reroute if the user is logged in - // const landingSite = isLoggedIn ? :
{landingpageData}
- const landingSite =
{landingpageDataBrowser}
; - - const loadedCheck = isLoaded ? ( -
- {landingSite} - {landingpageDataMobile} -
- ) : ( -
- ); - - return
{loadedCheck}
; -}; -export default LandingPage; diff --git a/frontend/src/views/MyView.jsx b/frontend/src/views/MyView.jsx deleted file mode 100755 index 42c72bc3..00000000 --- a/frontend/src/views/MyView.jsx +++ /dev/null @@ -1,2147 +0,0 @@ -import React, { useEffect } from "react"; -import { useInterval } from "react-powerhooks"; - -import Grid from "@material-ui/core/Grid"; -import Paper from "@material-ui/core/Paper"; -import Tooltip from "@material-ui/core/Tooltip"; -import Divider from "@material-ui/core/Divider"; -import Button from "@material-ui/core/Button"; -import TextField from "@material-ui/core/TextField"; -import FormControl from "@material-ui/core/FormControl"; -import IconButton from "@material-ui/core/IconButton"; -import Menu from "@material-ui/core/Menu"; -import MenuItem from "@material-ui/core/MenuItem"; -import FormControlLabel from "@material-ui/core/FormControlLabel"; -import Chip from "@material-ui/core/Chip"; -import Switch from "@material-ui/core/Switch"; -import Typography from "@material-ui/core/Typography"; -import Zoom from "@material-ui/core/Zoom"; - -import CircularProgress from "@material-ui/core/CircularProgress"; -import CachedIcon from "@material-ui/icons/Cached"; -import GetAppIcon from "@material-ui/icons/GetApp"; -import AppsIcon from "@material-ui/icons/Apps"; -import EditIcon from "@material-ui/icons/Edit"; -import MoreVertIcon from "@material-ui/icons/MoreVert"; -import PlayArrowIcon from "@material-ui/icons/PlayArrow"; -import AddIcon from "@material-ui/icons/Add"; -import PublishIcon from "@material-ui/icons/Publish"; -//import JSONPretty from 'react-json-pretty'; -//import JSONPrettyMon from 'react-json-pretty/dist/monikai' -import ReactJson from "react-json-view"; - -import { Link } from "react-router-dom"; -import { useAlert } from "react-alert"; -import ChipInput from "material-ui-chip-input"; - -import Dialog from "@material-ui/core/Dialog"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import CloudDownloadIcon from "@material-ui/icons/CloudDownload"; -import BubbleChartIcon from "@material-ui/icons/BubbleChart"; -import RestoreIcon from "@material-ui/icons/Restore"; - -import mobileImage from "../assets/img/mobile.svg"; -import bagImage from "../assets/img/bag.svg"; -import bookImage from "../assets/img/book.svg"; - -import { - DataGrid, - GridToolbarContainer, - GridDensitySelector, - GridToolbar, -} from "@mui/x-data-grid"; - -import { makeStyles } from "@material-ui/core/styles"; - -import ListIcon from "@material-ui/icons/List"; -import GridOnIcon from "@material-ui/icons/GridOn"; - -const inputColor = "#383B40"; -const surfaceColor = "#27292D"; - -const flexContainerStyle = { - display: "flex", - flexDirection: "row", - justifyContent: "left", - alignContent: "space-between", -}; - -const flexBoxStyle = { - height: 125, - borderRadius: 4, - boxSizing: "border-box", - letterSpacing: "0.4px", - color: "#D6791E", - margin: 10, - flex: 1, -}; - -//const activeWorkflowStyle = {backgroundColor: "#FFF5EE"} -//const notificationStyle = {backgroundColor: "#E5F9FF"} -//const activeWorkflowStyle = {backgroundColor: "#3d3f43"} -const availableWorkflowStyle = { backgroundColor: "#3d3f43" }; -const notificationStyle = { backgroundColor: "#3d3f43" }; -const activeWorkflowStyle = { backgroundColor: "#3d3f43" }; - -const flexContentStyle = { - display: "flex", - flexDirection: "row", -}; - -const iconStyle = { - width: "75px", - height: "75px", - padding: "20px", -}; - -const fontSize_16 = { fontSize: "16px" }; -const counterStyle = { fontSize: "36px", fontWeight: "bold" }; -const blockRightStyle = { - textAlign: "right", - padding: "20px 20px 0px 0px", - width: "100%", -}; - -export const validateJson = (showResult) => { - //showResult = showResult.split(" None").join(" \"None\"") - showResult = showResult.split(" False").join(" false"); - showResult = showResult.split(" True").join(" true"); - - var jsonvalid = true; - try { - const tmp = String(JSON.parse(showResult)); - if (!showResult.includes("{") && !showResult.includes("[")) { - jsonvalid = false; - } - } catch (e) { - showResult = showResult.split("'").join('"'); - - try { - const tmp = String(JSON.parse(showResult)); - if (!showResult.includes("{") && !showResult.includes("[")) { - jsonvalid = false; - } - } catch (e) { - jsonvalid = false; - } - } - - const result = jsonvalid ? JSON.parse(showResult) : showResult; - //console.log("VALID: ", jsonvalid, result) - return { - valid: jsonvalid, - result: result, - }; -}; - -const MyView = (props) => { - const { globalUrl, isLoggedIn, isLoaded, userdata } = props; - document.title = "Shuffle - Workflows"; - - const alert = useAlert(); - - var upload = ""; - const [file, setFile] = React.useState(""); - - const [workflows, setWorkflows] = React.useState([]); - const [selectedWorkflow, setSelectedWorkflow] = React.useState({}); - const [selectedExecution, setSelectedExecution] = React.useState({}); - const [workflowExecutions, setWorkflowExecutions] = React.useState([]); - const [firstrequest, setFirstrequest] = React.useState(true); - const [workflowDone, setWorkflowDone] = React.useState(false); - const [, setTrackingId] = React.useState(""); - const [selectedWorkflowId, setSelectedWorkflowId] = React.useState(""); - - const [collapseJson, setCollapseJson] = React.useState(false); - const [field1, setField1] = React.useState(""); - const [field2, setField2] = React.useState(""); - const [downloadUrl, setDownloadUrl] = React.useState( - "https://github.com/frikky/shuffle-workflows" - ); - const [downloadBranch, setDownloadBranch] = React.useState("master"); - const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] = - React.useState(false); - - const [modalOpen, setModalOpen] = React.useState(false); - const [newWorkflowName, setNewWorkflowName] = React.useState(""); - const [newWorkflowDescription, setNewWorkflowDescription] = - React.useState(""); - const [newWorkflowTags, setNewWorkflowTags] = React.useState([]); - const [update, setUpdate] = React.useState("test"); - const [deleteModalOpen, setDeleteModalOpen] = React.useState(false); - const [editingWorkflow, setEditingWorkflow] = React.useState({}); - const [executionLoading, setExecutionLoading] = React.useState(false); - const [view, setView] = React.useState("grid"); - const { start, stop } = useInterval({ - duration: 5000, - startImmediate: false, - callback: () => { - //getWorkflowExecution(selectedWorkflow.id) - }, - }); - - // DEBUG HERE - const handleClickLogout = () => {}; - - const deleteModal = deleteModalOpen ? ( - { - setDeleteModalOpen(false); - setSelectedWorkflowId(""); - }} - PaperProps={{ - style: { - backgroundColor: surfaceColor, - color: "white", - minWidth: 500, - }, - }} - > - -
- Are you sure?
- Other workflows relying on this one may stop working -
- - - - - -
- ) : null; - - const getAvailableWorkflows = () => { - fetch(globalUrl + "/api/v1/workflows", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for workflows :O!"); - return; - } - return response.json(); - }) - .then((responseJson) => { - setSelectedExecution({}); - setWorkflowExecutions([]); - - if (responseJson !== undefined) { - setWorkflows(responseJson); - setWorkflowDone(true); - } else { - if (isLoggedIn) { - alert.error("An error occurred while loading workflows"); - } else { - handleClickLogout(); - } - - return; - } - - if (responseJson.length > 0) { - setSelectedWorkflow(responseJson[0]); - //getWorkflowExecution(responseJson[0].id) - } - }) - .catch((error) => { - alert.error(error.toString()); - }); - }; - - useEffect(() => { - if (workflows.length <= 0 && firstrequest) { - setFirstrequest(false); - getAvailableWorkflows(); - } - }); - - const viewStyle = { - color: "#ffffff", - width: "100%", - display: "flex", - minWidth: 1024, - maxWidth: 1024, - margin: "auto", - /*maxHeight: "90vh",*/ - }; - - const emptyWorkflowStyle = { - paddingTop: "200px", - width: 1024, - margin: "auto", - }; - - const boxStyle = { - padding: "20px 20px 20px 20px", - width: "100%", - height: "250px", - color: "white", - backgroundColor: surfaceColor, - display: "flex", - flexDirection: "column", - }; - - const scrollStyle = { - marginTop: "10px", - overflow: "scroll", - height: "90%", - overflowX: "hidden", - overflowY: "auto", - }; - - const paperAppContainer = { - display: "flex", - flexWrap: "wrap", - alignContent: "space-between", - }; - - const paperAppStyle = { - minHeight: 130, - width: "100%", - color: "white", - backgroundColor: surfaceColor, - padding: "12px 12px 0px 15px", - borderRadius: 5, - display: "flex", - boxSizing: "border-box", - position: "relative", - }; - - const gridContainer = { - height: "auto", - color: "white", - margin: "10px", - backgroundColor: surfaceColor, - }; - - const workflowActionStyle = { - flex: "1", - display: "flex", - width: 150, - height: 44, - justifyContent: "space-between", - overflow: "hidden", - }; - - const getWorkflowExecution = (id) => { - setExecutionLoading(true); - fetch(globalUrl + "/api/v1/workflows/" + id + "/executions", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - setExecutionLoading(false); - if (response.status !== 200) { - console.log("Status not 200 for WORKFLOW EXECUTION :O!"); - } - - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success === false) { - alert.error("Failed getting executions"); - } else { - if (responseJson.length > 0) { - setSelectedExecution(responseJson[0]); - setWorkflowExecutions(responseJson); - } else { - //alert.info("Couldn't find executions for the workflow") - setSelectedExecution({}); - setWorkflowExecutions([]); - } - } - }) - .catch((error) => { - setExecutionLoading(false); - alert.error(error.toString()); - }); - }; - - const abortExecution = (workflowid, executionid) => { - alert.success("Aborting execution"); - fetch( - globalUrl + - "/api/v1/workflows/" + - workflowid + - "/executions/" + - executionid + - "/abort", - { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - } - ) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for WORKFLOW EXECUTION :O!"); - } - //getWorkflowExecution(workflowid) - - return response.json(); - }) - .catch((error) => { - alert.error(error.toString()); - }); - }; - - const executeWorkflow = (id) => { - alert.show("Executing workflow " + id); - setTrackingId(id); - fetch(globalUrl + "/api/v1/workflows/" + id + "/execute", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for WORKFLOW EXECUTION :O!"); - } - - return response.json(); - }) - .then((responseJson) => { - if (!responseJson.success) { - alert.error(responseJson.reason); - } - }) - .catch((error) => { - alert.error(error.toString()); - }); - - if (id === selectedWorkflow.id) { - sleep(2000).then(() => { - stop(); - start(); - }); - } - }; - - function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); - } - - const exportAllWorkflows = () => { - for (var key in workflows) { - exportWorkflow(workflows[key]); - } - }; - - const exportWorkflow = (data) => { - console.log("export"); - let dataStr = JSON.stringify(data); - - let dataUri = - "data:application/json;charset=utf-8," + encodeURIComponent(dataStr); - let exportFileDefaultName = data.name + ".json"; - - data["owner"] = ""; - for (var key in data.triggers) { - const trigger = data.triggers[key]; - if (trigger.app_name === "Shuffle Workflow") { - if (trigger.parameters.length > 2) { - trigger.parameters[2].value = ""; - } - } - - if (trigger.status == "running") { - trigger.status = "stopped"; - } - } - - for (var key in data.actions) { - data.actions[key].authentication_id = ""; - } - - //return - - data["org"] = []; - data["org_id"] = ""; - data.execution_org = { id: "" }; - console.log(data); - - let linkElement = document.createElement("a"); - linkElement.setAttribute("href", dataUri); - linkElement.setAttribute("download", exportFileDefaultName); - linkElement.click(); - }; - - const copyWorkflow = (data) => { - data = JSON.parse(JSON.stringify(data)); - alert.success("Copying workflow " + data.name); - console.log("data: ", data); - data.id = ""; - data.name = data.name + "_copy"; - //return - - fetch(globalUrl + "/api/v1/workflows", { - 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 workflows :O!"); - return; - } - return response.json(); - }) - .then((responseJson) => { - getAvailableWorkflows(); - }) - .catch((error) => { - alert.error(error.toString()); - }); - }; - - const deleteWorkflow = (id) => { - alert.success("Deleted workflow " + id); - fetch(globalUrl + "/api/v1/workflows/" + 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 setting workflows :O!"); - } - - return response.json(); - }) - .then((responseJson) => { - getAvailableWorkflows(); - }) - .catch((error) => { - alert.error(error.toString()); - }); - }; - - const getWorkflowMeta = (data) => { - let triggers = 0; - let schedules = 0; - let webhooks = 0; - let subflows = 0; - if ( - data.triggers !== undefined && - data.triggers !== null && - data.triggers.length > 0 - ) { - triggers = data.triggers.length; - for (let key in data.triggers) { - if (data.triggers[key].app_name === "Webhook") { - webhooks += 1; - //webhookImg = data.triggers[key].large_image - } else if (data.triggers[key].app_name === "Schedule") { - schedules += 1; - //scheduleImg = data.triggers[key].large_image - } else if (data.triggers[key].app_name === "Subflow") { - subflows += 1; - } - } - } - - return [triggers, schedules, webhooks, subflows]; - }; - - // dropdown with copy etc I guess - const WorkflowPaper = (props) => { - const { data } = props; - const [open, setOpen] = React.useState(false); - const [anchorEl, setAnchorEl] = React.useState(null); - - var boxWidth = "2px"; - if (selectedWorkflow.id === data.id) { - boxWidth = "4px"; - } - - var boxColor = "#FECC00"; - if (data.is_valid) { - boxColor = "#86c142"; - } - - if (!data.previously_saved) { - boxColor = "#f85a3e"; - } - - const menuClick = (event) => { - setOpen(!open); - setAnchorEl(event.currentTarget); - }; - - var parsedName = data.name; - if ( - parsedName !== undefined && - parsedName !== null && - parsedName.length > 25 - ) { - parsedName = parsedName.slice(0, 25) + ".."; - } - - const actions = data.actions !== null ? data.actions.length : 0; - const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data); - - return ( - - -
- - - - {parsedName} - - - - - - - - {actions} - - - - - - - - {triggers} - - - - - - - - - - {subflows} - - - - {/* - - - - - - - : null} - {schedules > 0 ? - - - - : null} - */} - - - {data.tags !== undefined - ? data.tags.map((tag, index) => { - if (index >= 3) { - return null; - } - - return ( - - ); - }) - : null} - - - {data.actions !== undefined && data.actions !== null ? ( - - - - - - { - setOpen(false); - setAnchorEl(null); - }} - > - { - setModalOpen(true); - setEditingWorkflow(data); - setNewWorkflowName(data.name); - setNewWorkflowDescription(data.description); - if (data.tags !== undefined && data.tags !== null) { - setNewWorkflowTags( - JSON.parse(JSON.stringify(data.tags)) - ); - } - }} - key={"change"} - > - {"Change details"} - - { - copyWorkflow(data); - setOpen(false); - }} - key={"copy"} - > - {"Copy"} - - { - exportWorkflow(data); - setOpen(false); - }} - key={"export"} - > - {"Export"} - - { - setDeleteModalOpen(true); - setSelectedWorkflowId(data.id); - setOpen(false); - }} - key={"delete"} - > - {"Delete"} - - - - - - - - - - - - ) : null} - - - ); - }; - - const executionPaper = (data) => { - var boxWidth = "2px"; - if (selectedExecution.execution_id === data.execution_id) { - boxWidth = "4px"; - } - - var boxColor = "orange"; - if ( - data.status === "ABORTED" || - data.status === "UNFINISHED" || - data.status === "FAILURE" - ) { - boxColor = "red"; - } else if (data.status === "FINISHED") { - boxColor = "green"; - } - - var t = new Date(data.started_at * 1000); - if (data.workflow.actions === null || data.workflow.actions === undefined) { - return null; - } - - if (data.workflow.actions === null || data.workflow.actions === undefined) { - return null; - } - - var actions = data.workflow.actions.length; - if (data.results !== null) { - var results = data.results.length; - } - - return ( - { - setSelectedExecution(data); - }} - > -
- - - -
-

- Status: {data.status} -

- Actions: {results}/{actions} -
-
- -
-
-
- - Started: {t.toISOString()} - -
-
-
- - ); - }; - - const dividerColor = "rgb(225, 228, 232)"; - - const resultPaperAppStyle = { - minHeight: "100px", - minWidth: "100%", - overflow: "hidden", - maxWidth: "100%", - marginTop: "5px", - color: "white", - backgroundColor: surfaceColor, - display: "flex", - }; - - function replaceAll(string, search, replace) { - return string.split(search).join(replace); - } - - const resultsPaper = (data) => { - var boxWidth = "2px"; - var boxColor = "orange"; - if ( - data.status === "ABORTED" || - data.status === "UNFINISHED" || - data.status === "FAILURE" - ) { - boxColor = "red"; - } else if (data.status === "FINISHED" || data.status === "SUCCESS") { - boxColor = "green"; - } else if (data.status === "SKIPPED" || data.status === "EXECUTING") { - boxColor = "yellow"; - } else { - boxColor = "green"; - } - - var t = new Date(data.started_at * 1000); - var showResult = data.result.trim(); - const validate = validateJson(showResult); - - if (validate.valid) { - showResult = ( - - ); - } else { - // FIXME - have everything parsed as json, either just for frontend - // or in the backend? - /* - const newdata = {"result": data.result} - showResult = - */ - } - - return ( - {}} - > -
- - - -

- Name: {data.action.label} -

-
- - App: {data.action.app_name}, Version: {data.action.app_version} - - - Action: {data.action.name}, Environment: {data.action.environment} - , Status: {data.status} - -
- - Started: {t.toISOString()} - -
- -
- - {showResult} - -
-
-
-
- ); - }; - - const resultsHandler = - Object.getOwnPropertyNames(selectedExecution).length > 0 && - selectedExecution.results !== null ? ( -
- {selectedExecution.results - .sort((a, b) => a.started_at - b.started_at) - .map((data, index) => { - return
{resultsPaper(data)}
; - })} -
- ) : ( -
No results yet
- ); - - const resultsLength = - Object.getOwnPropertyNames(selectedExecution).length > 0 && - selectedExecution.results !== null - ? selectedExecution.results.length - : 0; - - const ExecutionDetails = () => { - var starttime = new Date(selectedExecution.started_at * 1000); - var endtime = new Date(selectedExecution.started_at * 1000); - - var parsedArgument = selectedExecution.execution_argument; - if ( - selectedExecution.execution_argument !== undefined && - selectedExecution.execution_argument.length > 0 - ) { - parsedArgument = replaceAll(parsedArgument, " None", ' "None"'); - } - - var arg = null; - if ( - selectedExecution.execution_argument !== undefined && - selectedExecution.execution_argument.length > 0 - ) { - var showResult = selectedExecution.execution_argument.trim(); - const validate = validateJson(showResult); - - arg = validate.valid ? ( - - ) : ( - showResult - ); - } - - var lastresult = null; - if ( - selectedExecution.result !== undefined && - selectedExecution.result.length > 0 - ) { - var showResult = selectedExecution.result.trim(); - const validate = validateJson(showResult); - lastresult = validate.valid ? ( - - ) : ( - showResult - ); - } - - /* -
- ID: {selectedExecution.execution_id} -
-
- Last node: {selectedExecution.workflow.actions.find(data => data.id === selectedExecution.last_node).actions[0].label} -
- */ - if ( - Object.getOwnPropertyNames(selectedExecution).length > 0 && - selectedExecution.workflow.actions !== null - ) { - return ( -
-
- Status: {selectedExecution.status} -
-
- Started: {starttime.toISOString()} -
-
- Finished: {endtime.toISOString()} -
- {/* -
- Last Result: {lastresult} -
- */} -
{arg}
- - {resultsHandler} -
- ); - } - - return executionLoading ? ( -
- -
- ) : ( -

- There are no executiondetails yet. Click "execute" to run your first - one. -

- ); - }; - - const ExecutionsView = () => { - if (workflowExecutions.length > 0) { - const sortedWorkflows = workflowExecutions - .sort((a, b) => a.started_at - b.started_at) - .reverse(); - - return ( -
- {sortedWorkflows.map((data) => { - return executionPaper(data); - })} -
- ); - } - return executionLoading ? ( -
- -
- ) : ( -

- Executions have been moved to the Workflow itself.
- - Click here to see them - -

- ); - }; - - // Can create and set workflows - const setNewWorkflow = ( - name, - description, - tags, - editingWorkflow, - redirect - ) => { - var method = "POST"; - var extraData = ""; - var workflowdata = {}; - - if (editingWorkflow.id !== undefined) { - console.log("Building original workflow"); - method = "PUT"; - extraData = "/" + editingWorkflow.id; - workflowdata = editingWorkflow; - - console.log("REMOVING OWNER"); - workflowdata["owner"] = ""; - // FIXME: Loop triggers and turn them off? - } - - workflowdata["name"] = name; - workflowdata["description"] = description; - if (tags !== undefined) { - workflowdata["tags"] = tags; - } - //console.log(workflowdata) - //return - - return fetch(globalUrl + "/api/v1/workflows" + extraData, { - method: method, - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(workflowdata), - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for workflows :O!"); - return; - } - return response.json(); - }) - .then((responseJson) => { - if (method === "POST" && redirect) { - window.location.pathname = "/workflows/" + responseJson["id"]; - } else if (!redirect) { - // Update :) - getAvailableWorkflows(); - } else { - alert.info("Successfully changed basic info for workflow"); - } - - return responseJson; - }) - .catch((error) => { - alert.error(error.toString()); - }); - }; - - const importFiles = (event) => { - console.log("Importing!"); - const file = event.target.value; - if (event.target.files.length > 0) { - for (var key in event.target.files) { - const file = event.target.files[key]; - if (file.type !== "application/json") { - if (file.type !== undefined) { - alert.error("File has to contain valid json"); - } - - continue; - } - - const reader = new FileReader(); - // Waits for the read - reader.addEventListener("load", (event) => { - var data = reader.result; - try { - data = JSON.parse(reader.result); - } catch (e) { - alert.error("Invalid JSON: " + e); - return; - } - - // Initialize the workflow itself - const ret = setNewWorkflow( - data.name, - data.description, - data.tags, - {}, - false - ) - .then((response) => { - if (response !== undefined) { - // SET THE FULL THING - data.id = response.id; - - // Actually create it - const ret = setNewWorkflow( - data.name, - data.description, - data.tags, - data, - false - ).then((response) => { - if (response !== undefined) { - alert.success("Successfully imported " + data.name); - } - }); - } - }) - .catch((error) => { - alert.error("Import error: " + error.toString()); - }); - }); - - // Actually reads - reader.readAsText(file); - } - } - - setLoadWorkflowsModalOpen(false); - }; - - const modalView = modalOpen ? ( - { - setModalOpen(false); - }} - PaperProps={{ - style: { - backgroundColor: surfaceColor, - color: "white", - minWidth: "800px", - }, - }} - > - -
- {editingWorkflow.id !== undefined ? "Editing" : "New"} workflow -
- - - -
-
-
- - - setNewWorkflowName(event.target.value)} - InputProps={{ - style: { - color: "white", - }, - }} - color="primary" - placeholder="Name" - margin="dense" - defaultValue={newWorkflowName} - fullWidth - /> - setNewWorkflowDescription(event.target.value)} - InputProps={{ - style: { - color: "white", - }, - }} - color="primary" - defaultValue={newWorkflowDescription} - placeholder="Description" - margin="dense" - fullWidth - /> - { - newWorkflowTags.push(chip); - setNewWorkflowTags(newWorkflowTags); - }} - onDelete={(chip, index) => { - newWorkflowTags.splice(index, 1); - setNewWorkflowTags(newWorkflowTags); - setUpdate("delete " + chip); - }} - /> - - - - - - -
- ) : null; - - const viewSize = { - workflowView: 4, - executionsView: 3, - executionResults: 4, - }; - - const workflowViewStyle = { - flex: viewSize.workflowView, - marginLeft: "10px", - marginRight: "10px", - }; - - if (viewSize.workflowView === 0) { - workflowViewStyle.display = "none"; - } - - const workflowButtons = ( - - {view === "grid" && ( - - - - )} - {view === "list" && ( - - - - )} - {workflows.length > 0 ? ( - - - - ) : null} - - - - (upload = ref)} - onChange={importFiles} - /> - {workflows.length > 0 ? ( - - - - ) : null} - - - - - ); - - const useStyles = makeStyles((theme) => ({ - root: { - border: 0, - "& .MuiDataGrid-columnsContainer": { - backgroundColor: theme.palette.type === "light" ? "#fafafa" : "#1d1d1d", - }, - "& .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": - { - borderRadius: 0, - color: "white", - }, - }, - })); - const classes = useStyles(); - - const WorkflowGridView = () => { - let workflowData = ""; - if (workflows.length > 0) { - const columns = [ - { field: "title", headerName: "Title", width: 330 }, - { - field: "actions", - headerName: "Actions", - width: 200, - sortable: false, - disableClickEventBubbling: true, - renderCell: (params) => { - const data = params.row.record; - let [triggers, schedules, webhooks, subflows] = - getWorkflowMeta(data); - - return ( - - - - - - - - - executeWorkflow(data.id)} - /> - - - - - {webhooks > 0 ? ( - - - - ) : null} - {schedules > 0 ? ( - - - - ) : null} - - ); - }, - }, - { - field: "tags", - headerName: "Tags", - width: 390, - sortable: false, - disableClickEventBubbling: true, - renderCell: (params) => { - const data = params.row.record; - return ( - - {data.tags !== undefined - ? data.tags.map((tag, index) => { - if (index >= 3) { - return null; - } - - return ( - - ); - }) - : null} - - ); - }, - }, - ]; - let rows = []; - rows = workflows.map((data, index) => { - let obj = { id: index + 1, title: data.name, record: data }; - return obj; - }); - workflowData = ( - - ); - } - return
{workflowData}
; - }; - - const WorkflowView = () => { - if (workflows.length === 0) { - return ( -
- -
-

Welcome to Shuffle

-
-
-

- Shuffle is a flexible, easy to use, automation platform - allowing users to integrate their services and devices freely. - It's made to significantly reduce the amount of manual labor, - and is focused on security applications.{" "} - - Click here to learn more. - -

-
-
- If you want to jump straight into it, click here to create your - first workflow: -
-
- - - - ..OR - - {workflowButtons} - -
-
-
- ); - } - - return ( -
-
-
-
-

Workflows

-
-
- -
-
-
-
- -
-
-
{workflows.length}
-
ACTIVE WORKFLOWS
-
-
-
-
-
-
- -
-
-
{workflows.length}
-
AVAILABE WORKFLOWS
-
-
-
-
-
-
- -
-
-
{workflows.length}
-
NOTIFICATIONS
-
-
-
-
- -
-
- - This is your workflow view.{" "} - - Learn more about Workflows - - -
-
{workflowButtons}
-
-
- {view === "grid" && ( - - {workflows.map((data, index) => { - return ; - })} - - )} - - {view === "list" && } - -
-
-
- ); - }; - - const importWorkflowsFromUrl = (url) => { - console.log("IMPORT WORKFLOWS FROM ", downloadUrl); - - const parsedData = { - url: url, - field_3: downloadBranch || "master", - }; - - if (field1.length > 0) { - parsedData["field_1"] = field1; - } - - if (field2.length > 0) { - parsedData["field_2"] = field2; - } - - alert.success("Getting specific workflows from your URL."); - var cors = "cors"; - fetch(globalUrl + "/api/v1/workflows/download_remote", { - method: "POST", - mode: "cors", - headers: { - Accept: "application/json", - }, - body: JSON.stringify(parsedData), - credentials: "include", - }) - .then((response) => { - if (response.status === 200) { - alert.success("Successfully loaded workflows from " + downloadUrl); - getAvailableWorkflows(); - } - - return response.json(); - }) - .then((responseJson) => { - console.log("DATA: ", responseJson); - if (!responseJson.success) { - if (responseJson.reason !== undefined) { - alert.error("Failed loading: " + responseJson.reason); - } else { - alert.error("Failed loading"); - } - } - }) - .catch((error) => { - alert.error(error.toString()); - }); - }; - - const handleGithubValidation = () => { - importWorkflowsFromUrl(downloadUrl); - setLoadWorkflowsModalOpen(false); - }; - - const workflowDownloadModalOpen = loadWorkflowsModalOpen ? ( - {}} - PaperProps={{ - style: { - backgroundColor: surfaceColor, - color: "white", - minWidth: "800px", - minHeight: "320px", - }, - }} - > - -
- Load workflows from github repo -
- - - -
-
-
- - Repository (supported: github, gitlab, bitbucket) - 0 - ? userdata.active_org.defaults.workflow_download_repo - : downloadUrl - } - InputProps={{ - style: { - color: "white", - height: "50px", - fontSize: "1em", - }, - }} - onChange={(e) => setDownloadUrl(e.target.value)} - placeholder="https://github.com/frikky/shuffle-apps" - fullWidth - /> - - Branch (default value is "master"): - -
- 0 - ? userdata.active_org.defaults.workflow_download_branch - : downloadBranch - } - InputProps={{ - style: { - color: "white", - height: "50px", - fontSize: "1em", - }, - }} - onChange={(e) => setDownloadBranch(e.target.value)} - placeholder="master" - fullWidth - /> -
- - Authentication (optional - private repos etc): - -
- setField1(e.target.value)} - type="username" - placeholder="Username / APIkey (optional)" - fullWidth - /> - setField2(e.target.value)} - type="password" - placeholder="Password (optional)" - fullWidth - /> -
-
- - - - -
- ) : null; - - const loadedCheck = - isLoaded && isLoggedIn && workflowDone ? ( -
- - {modalView} - {deleteModal} - {workflowDownloadModalOpen} -
- ) : ( -
- - Loading Workflows -
- ); - - // Maybe use gridview or something, idk - return
{loadedCheck}
; -}; - -export default MyView; diff --git a/frontend/src/views/RegisterLink.jsx b/frontend/src/views/RegisterLink.jsx deleted file mode 100755 index 1e4827c6..00000000 --- a/frontend/src/views/RegisterLink.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; - -import Paper from "@material-ui/core/Paper"; - -const bodyDivStyle = { - margin: "auto", - textAlign: "center", - width: "768px", -}; - -//const tmpdata = { -// "username": "frikky", -// "firstname": "fred", -// "lastname": "ode", -// "title": "topkek", -// "companyname": "company here", -// "email": "your email pls", -// "phone": "PHONE!!", -//} - -// FIXME - add fetch for data fields -// FIXME - remove tmpdata -// FIXME: Use isLoggedIn :) -const Settings = (defaultprops) => { - const { globalUrl, isLoaded, surfaceColor } = defaultprops; - - const params = useParams(); - var props = JSON.parse(JSON.stringify(defaultprops)) - props.match = {} - props.match.params = params - - const [firstRequest, setFirstRequest] = useState(true); - const boxStyle = { - flex: "1", - marginLeft: "10px", - marginRight: "10px", - paddingLeft: "30px", - paddingRight: "30px", - paddingBottom: "30px", - paddingTop: "30px", - backgroundColor: surfaceColor, - color: "white", - display: "flex", - flexDirection: "column", - }; - - const registerCall = () => { - const url = globalUrl + "/api/v1/register/" + props.match.params.key; - fetch(url, { - method: "GET", - credentials: "include", - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - }) - .then((response) => - response.json().then((responseJson) => { - console.log(responseJson); - }) - ) - .catch((error) => { - console.log("SOMETHING WRONG"); - }); - }; - - // This should "always" have data - useEffect(() => { - if (firstRequest) { - setFirstRequest(false); - registerCall(); - } - }); - - // Random names for type & autoComplete. Didn't research :^) - const landingpageData = ( -
- -

Registration verification

-

Thanks for verifying, redirecting you to our login!

-
-
- ); - - const loadedCheck = isLoaded ? ( -
{landingpageData}
- ) : ( -
- ); - - return
{loadedCheck}
; -}; -export default Settings; diff --git a/frontend/src/views/RegisterPage.jsx b/frontend/src/views/RegisterPage.jsx deleted file mode 100755 index 34df7666..00000000 --- a/frontend/src/views/RegisterPage.jsx +++ /dev/null @@ -1,176 +0,0 @@ -/* eslint-disable react/no-multi-comp */ -import React, { useState } from "react"; - -import DialogTitle from "@material-ui/core/DialogTitle"; -import Dialog from "@material-ui/core/Dialog"; -import TextField from "@material-ui/core/TextField"; -import Button from "@material-ui/core/Button"; - -const LoginDialog = (props) => { - const { - classes, - onClose, - open, - globalUrl, - isLoggedIn, - setIsLoggedIn, - ...other - } = props; - - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - //const [selectedValue, setSelectedValue] = useState(false); - - // Used to swap from login to register. True = login, false = register - const [loginCheck, setLoginCheck] = useState(true); - - // Error messages etc - const [loginInfo, setLoginInfo] = useState(""); - - const handleValidateForm = () => { - return username.length > 1 && password.length > 8; - }; - - const onSubmit = (e) => { - e.preventDefault(); - - // Just use this one? - var data = - '{"username": "' + username + '", "password": "' + password + '"}'; - var baseurl = globalUrl; - if (loginCheck) { - var url = baseurl + "/login"; - fetch(url, { - method: "POST", - body: data, - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => - response.json().then((responseJson) => { - console.log(responseJson); - //console.log(e) - if (responseJson["success"] === false) { - setLoginInfo(responseJson["reason"]); - } else { - setLoginInfo("Successful login :)"); - onClose(); - setIsLoggedIn(true); - } - }) - ) - .catch((error) => { - setLoginInfo("Error in userdata"); - }); - } else { - url = baseurl + "/register"; - fetch(url, { - method: "POST", - body: data, - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => - response.json().then((responseJson) => { - if (responseJson["success"] === false) { - setLoginInfo(responseJson["reason"]); - } else { - setLoginInfo("Successful register. Please check your mail :)"); - onClose(); - setIsLoggedIn(true); - } - }) - ) - .catch((error) => { - setLoginInfo("Error in userdata"); - }); - } - }; - - const onChangeUser = (e) => { - setUsername(e.target.value); - }; - - const onChangePass = (e) => { - setPassword(e.target.value); - }; - - const onClickRegister = () => { - setLoginCheck(!loginCheck); - }; - - //var loginChange = loginCheck ? (

Want to register? Click here.

) : (

Go back to login? Click here.

); - var formtitle = loginCheck ?
Login
:
Register
; - var formButton = loginCheck ? ( -
Click to Register
- ) : ( -
Click to Login
- ); - - return ( - - {formtitle} -
- Username -
- -
- Password -
- -
-
- - - -
- {loginInfo} -
-
- -
-
- ); -}; - -export default LoginDialog; diff --git a/frontend/src/views/Schedules.jsx b/frontend/src/views/Schedules.jsx deleted file mode 100755 index 88c92251..00000000 --- a/frontend/src/views/Schedules.jsx +++ /dev/null @@ -1,232 +0,0 @@ -import React, { useEffect } from "react"; - -import Paper from "@material-ui/core/Paper"; -import Grid from "@material-ui/core/Grid"; -import ButtonBase from "@material-ui/core/ButtonBase"; -import List from "@material-ui/core/List"; -import ListItem from "@material-ui/core/ListItem"; -import Button from "@material-ui/core/Button"; -//import Breadcrumbs from '@material-ui/core/Breadcrumbs'; - -const Schedules = (props) => { - const { globalUrl } = props; - - //const [schedules, setSchedules] = React.useState(scheduledata); - const [schedules, setSchedules] = React.useState({}); - - const getAvailableSchedules = () => { - fetch(globalUrl + "/api/v1/schedules", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }) - .then((response) => response.json()) - .then((responseJson) => { - setSchedules(responseJson); - }) - .catch((error) => { - console.log(error); - }); - }; - - // FIXME - add automated redirection, as empty apps look horrible currently - const newSchedule = () => { - fetch(globalUrl + "/api/v1/schedules/new", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(), - }) - .then((response) => response.json()) - .then((responseJson) => { - console.log(responseJson); - setSchedules({}); - }) - .catch((error) => { - console.log(error); - }); - }; - - const deleteSchedule = (id) => { - if (id === undefined) { - return; - } - - fetch(globalUrl + "/api/v1/schedules/" + id + "/delete", { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }) - .then((response) => response.json()) - .then((responseJson) => { - setSchedules({}); - }) - .catch((error) => { - console.log(error); - }); - }; - - // FIXME - use this? - //const getNewScheduleInfo = () => { - // fetch(globalUrl+"/api/v1/schedules", { - // method: 'GET', - // headers: { - // 'Content-Type': 'application/json', - // 'Accept': 'application/json', - // }, - // }) - // .then((response) => response.json()) - // .then((responseJson) => { - // setSchedules(responseJson) - // }) - // .catch(error => { - // console.log(error) - // }); - //} - - useEffect(() => { - if (Object.getOwnPropertyNames(schedules).length <= 0) { - getAvailableSchedules(); - } - }); - - const bodyDivStyle = { - marginLeft: "20px", - marginRight: "20px", - width: "1350px", - minWidth: "1350px", - maxWidth: "1350px", - }; - - const scheduleApp = (app) => { - console.log(app); - return ( - - - - - - - - - -
-

{app.name}

-
-
{app.description}
-
- {app.action} -
-
-
- ); - }; - - const splitter = ( -
- ); - - const hrefStyle = { - color: "#385f71", - textDecoration: "none", - }; - - // FIXME - add Schedule modal - const schedulePaper = (schedule) => { - return ( -
- -
- {scheduleApp(schedule.appinfo.sourceapp)} -
-
ARROW
-
- {scheduleApp(schedule.appinfo.destinationapp)} -
- {splitter} -
- - - - - - - - - - -
-
-
- ); - }; - - console.log(schedules); - console.log(schedules); - console.log(schedules.schedules); - const schedulemap = - Object.getOwnPropertyNames(schedules).length > 0 && - schedules.schedules && - schedules.schedules.length > 0 ? ( -
{schedules.schedules.map((data) => schedulePaper(data))}
- ) : ( -
- -
- ); - - const scheduleView = - Object.getOwnPropertyNames(schedules).length > 0 ? ( -
- - {schedulemap} -
- ) : null; - - // Maybe use gridview or something, idk - return
{scheduleView}
; -}; - -export default Schedules; diff --git a/frontend/src/views/TempDashboard.jsx b/frontend/src/views/TempDashboard.jsx deleted file mode 100644 index a34ac730..00000000 --- a/frontend/src/views/TempDashboard.jsx +++ /dev/null @@ -1,298 +0,0 @@ -import React from "react"; -import { Grid, Container, Divider } from "@mui/material"; - -import { makeStyles } from "@mui/material/styles"; -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import Typography from "@material-ui/core/Typography"; - -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; -import TableCell from "@material-ui/core/TableCell"; -import TableContainer from "@material-ui/core/TableContainer"; -import TableHead from "@material-ui/core/TableHead"; -import TableRow from "@material-ui/core/TableRow"; -import Paper from "@material-ui/core/Paper"; - -import { LineChart, LineSeries, BarChart } from "reaviz"; -import { GridStripe } from "reaviz"; -//import { GridlineSeries } from "reaviz"; - -import InputLabel from '@material-ui/core/InputLabel'; -import FormControl from '@material-ui/core/FormControl'; -import Select from '@material-ui/core/Select'; -import MenuItem from '@material-ui/core/MenuItem'; - -const data = [ - { - key: new Date("11/29/2019"), - data: 10, - }, - { - key: new Date("11/30/2019"), - data: 14, - }, - { - key: new Date("12/01/2019"), - data: 5, - }, - { - key: new Date("12/02/2019"), - data: 18, - }, -]; - -const useStyles1 = makeStyles((theme) => ({ - formControl: { - margin: theme.spacing(1), - minWidth: 120, - }, - selectEmpty: { - marginTop: theme.spacing(2), - }, -})); - - -const useStyles = makeStyles({ - table: { - minWidth: 650, - }, - root: { - minWidth: 275, - }, - bullet: { - display: "inline-block", - margin: "0 2px", - transform: "scale(0.8)", - }, - title: { - fontSize: 14, - }, - pos: { - marginBottom: 12, - }, -}); - -function createData(name, calories, fat, carbs, protein) { - return { name, calories, fat, carbs, protein }; -} - -const rows = [ - createData("Frozen yoghurt", 159, 6.0, 24, 4.0), - createData("Ice cream sandwich", 237, 9.0, 37, 4.3), - createData("Eclair", 262, 16.0, 24, 6.0), - createData("Cupcake", 305, 3.7, 67, 4.3), - createData("Gingerbread", 356, 16.0, 49, 3.9), -]; - -const DashboardPage = () => { - const classes = useStyles(); - const classes1 = useStyles1(); - - const [age, setAge] = React.useState(0); - - const handleChange = (event) => { - setAge(event.target.value); - - }; - - return ( - - - -
- - Dashboard - -
- - Organization - - -
-
-
- -
- - - - - - Total workflows executions - - - 456 - - - - - - - - - Total Apps executions - - - 587 - - - - - - - - - Total failed executions - - - 999 - - - - - - - - - - - } - series={} - /> - - - - - - - - - Dessert (100g serving) - Calories - Fat (g) - Carbs (g) - Protein (g) - - - - {rows.map((row) => ( - - - {row.name} - - {row.calories} - {row.fat} - {row.carbs} - {row.protein} - - ))} - -
-
-
-
-
- ); -}; - -export default DashboardPage; diff --git a/frontend/src/views/Webhooks.jsx b/frontend/src/views/Webhooks.jsx deleted file mode 100755 index 64a463b9..00000000 --- a/frontend/src/views/Webhooks.jsx +++ /dev/null @@ -1,316 +0,0 @@ -import React, { useEffect } from "react"; - -import Paper from "@material-ui/core/Paper"; -import Grid from "@material-ui/core/Grid"; -import ButtonBase from "@material-ui/core/ButtonBase"; -import Button from "@material-ui/core/Button"; -import List from "@material-ui/core/List"; -import ListItem from "@material-ui/core/ListItem"; -import TextField from "@material-ui/core/TextField"; -import Select from "@material-ui/core/Select"; -import MenuItem from "@material-ui/core/MenuItem"; - -import Dialog from "@material-ui/core/Dialog"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; - -import WebhookImage from "../assets/img/webhook.png"; -import KafkaImage from "../assets/img/kafka.png"; - -const Webhooks = (props) => { - const { globalUrl, isLoaded } = props; - const validtypes = ["webhook"]; - - //const [hooks, setSchedules] = React.useState(hookdata); - const [hooks, setHooks] = React.useState([]); - const [modalOpen, setModalOpen] = React.useState(false); - const [newHookName, setNewHookName] = React.useState(""); - const [newHookDescription, setNewHookDescription] = React.useState(""); - const [newHookType, setNewHookType] = React.useState(""); - const [firstrequest, setFirstrequest] = React.useState(true); - const [, setModalError] = React.useState(""); - - useEffect(() => { - if (firstrequest) { - setFirstrequest(false); - getAvailableHooks(); - } - }); - - const newHook = () => { - if (newHookName.length === 0) { - setModalError("Missing name in modal"); - return; - } - - if (!validtypes.includes(newHookType)) { - setModalError( - newHookType + " is not a valid type. Try this: " + validtypes - ); - } - - fetch(globalUrl + "/api/v1/hooks/new", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ - name: newHookName, - description: newHookDescription, - type: newHookType, - }), - credentials: "include", - }) - .then((response) => response.json()) - .then((responseJson) => { - console.log(responseJson); - setHooks([]); - }) - .catch((error) => { - console.log(error); - }); - }; - - const getAvailableHooks = () => { - fetch(globalUrl + "/api/v1/hooks", { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => response.json()) - .then((responseJson) => { - setHooks(responseJson); - }) - .catch((error) => { - console.log(error); - // window.location.pathname = "/" - }); - }; - - const deleteHook = (id) => { - if (id === undefined) { - return; - } - - fetch(globalUrl + "/api/v1/hooks/" + id + "/delete", { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - }) - .then((response) => response.json()) - .then((responseJson) => { - setHooks([]); - }) - .catch((error) => { - console.log(error); - }); - }; - - const bodyDivStyle = { - marginLeft: "20px", - marginRight: "20px", - width: "1350px", - minWidth: "1350px", - maxWidth: "1350px", - }; - - const hookApp = (app) => { - // Might be more options, but should be webhook or MQ - const appPicture = - app.type === "webhook" ? ( - webhook - ) : ( - MQ - ); - - return ( - - - {appPicture} - - {splitter} - - - -
-

{app.info.name}

-
-
Desc: {app.info.description}
-
Status: {app.status}
-
- {app.action} -
-
-
- ); - }; - - const splitter = ( -
- ); - - const hrefStyle = { - color: "#385f71", - textDecoration: "none", - }; - - // FIXME - add Schedule modal - const hookPaper = (hook) => { - return ( -
- -
{hookApp(hook)}
- {splitter} -
- - - - - - - - - - -
-
-
- ); - }; - - const modalView = modalOpen ? ( - { - setModalOpen(false); - }} - > - Hook configuration - - { - setNewHookName(event.target.value); - }} - color="primary" - placeholder="Name" - margin="dense" - fullWidth - /> - { - setNewHookDescription(event.target.value); - }} - color="primary" - placeholder="Description" - margin="dense" - fullWidth - /> - - - - - - - - - ) : null; - - const hookmap = - hooks.length > 0 ? ( -
{hooks.map((data) => hookPaper(data))}
- ) : ( -
- -
- ); - - const hookView = ( -
- - {hookmap} -
- ); - - const loadedCheck = isLoaded ? ( -
- {modalView} - {hookView} -
- ) : ( -
- ); - - // Maybe use gridview or something, idk - return
{loadedCheck}
; -}; - -export default Webhooks; diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index ab6eddd6..a892dbb3 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -75,14 +75,8 @@ import { ArrowRight as ArrowRightIcon, } from "@mui/icons-material"; -//import NestedMenuItem from "material-ui-nested-menu-item"; -//import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; - -//https://next.material-ui.com/components/material-icons/ import { DataGrid, GridToolbar } from "@mui/x-data-grid"; -//import JSONPretty from 'react-json-pretty'; -//import JSONPrettyMon from 'react-json-pretty/dist/monikai' import Dropzone from "../components/Dropzone.jsx"; import { useNavigate, Link } from "react-router-dom"; @@ -1747,13 +1741,6 @@ const Workflows = (props) => { {"Duplicate Workflow"} - {/*= 0} style={{backgroundColor: theme.palette.inputColor, color: "white"}} onClick={() => { - //copyWorkflow(data) - //setOpen(false) - }} key={"duplicate"}> - - {"Copy to Child Org"} - */} { From 1461d6ad6b6e67e4e1647c8b9331911891858c7c Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 17 Aug 2023 02:13:38 +0200 Subject: [PATCH 023/160] Fixed another new workflow recommendation issue --- backend/go-app/go.mod | 2 +- frontend/src/components/EditWorkflow.jsx | 118 ++++++++++++++--------- frontend/src/components/WorkflowGrid.jsx | 95 ++++++++++-------- 3 files changed, 126 insertions(+), 89 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 5f0ffdff..e400b547 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -//replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared go 1.19 diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index c862f22b..38ddbcf5 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -3,6 +3,7 @@ import theme from '../theme.jsx'; import { isMobile } from "react-device-detect" import { MuiChipsInput } from "mui-chips-input"; import UsecaseSearch from "../components/UsecaseSearch.jsx" +import WorkflowGrid from "../components/WorkflowGrid.jsx" import dayjs from 'dayjs'; import { @@ -62,12 +63,14 @@ const EditWorkflow = (props) => { const [submitLoading, setSubmitLoading] = React.useState(false); const [showMoreClicked, setShowMoreClicked] = React.useState(false); - const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) + const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) + const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : []) + const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "") + const [selectedUsecases, setSelectedUsecases] = React.useState(workflow.usecase_ids !== undefined && workflow.usecase_ids !== null ? JSON.parse(JSON.stringify(workflow.usecase_ids)) : []); const [foundWorkflowId, setFoundWorkflowId] = React.useState("") const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "") - const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "") const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day')) // Gets the generated workflow @@ -154,58 +157,59 @@ const EditWorkflow = (props) => { PaperProps={{ style: { color: "white", - minWidth: isMobile ? "90%" : 550, - maxWidth: isMobile ? "90%" : 550, + minWidth: isMobile ? "90%" : 650, + maxWidth: isMobile ? "90%" : 650, minHeight: 400, + paddingTop: 25, //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, }, }} > -
+
-
- - {newWorkflow ? "New" : "Editing"} workflow - - {newWorkflow === true ? null : -
- - - - - -
- } +
+ + {newWorkflow ? "New" : "Editing"} workflow + + {newWorkflow === true ? null : +
+ + + + +
- - Workflows can be built from scratch, or from templates. Usecases can help you discover next steps, and you can search for them directly. Learn more - - {showUpload === true ? -
- - - -
- : null} + } +
+ + Workflows can be built from scratch, or from templates. Usecases can help you discover next steps, and you can search for them directly. Learn more + + {showUpload === true ? +
+ + + +
+ : null}
{/*newWorkflow === true ?
@@ -220,8 +224,8 @@ const EditWorkflow = (props) => {
- -
+ +
{ setName(event.target.value) @@ -475,7 +479,8 @@ const EditWorkflow = (props) => {
- + + + + {newWorkflow === true && name.length > 5 ? +
+ +
+ : null} ) diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index e4542e06..fb19e806 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -25,12 +25,9 @@ import WorkflowPaperNew from "../components/WorkflowPaperNew.jsx" const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const AppGrid = props => { - const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, } = props - - const isCloud = - window.location.host === "localhost:3002" || - window.location.host === "shuffler.io"; + const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs //const [apps, setApps] = React.useState([]); @@ -38,7 +35,9 @@ const AppGrid = props => { const [formMail, setFormMail] = React.useState(""); const [message, setMessage] = React.useState(""); const [formMessage, setFormMessage] = React.useState(""); - const [usecases, setUsecases] = React.useState([]); + const [usecases, setUsecases] = React.useState([]); + + const [localMessage, setLocalMessage] = React.useState(""); const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,} @@ -153,10 +152,10 @@ const AppGrid = props => { return response.json(); }) .then((responseJson) => { - if (responseJson.success !== false) { - console.log("Usecases: ", responseJson) - //handleKeysetting(responseJson, workflows) - } + if (responseJson.success !== false) { + console.log("Usecases: ", responseJson) + //handleKeysetting(responseJson, workflows) + } }) .catch((error) => { //alert.error("ERROR: " + error.toString()); @@ -166,8 +165,10 @@ const AppGrid = props => { useEffect(() => { fetchUsecases() + }, []) + // value={currentRefinement} const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { useEffect(() => { @@ -182,16 +183,23 @@ const AppGrid = props => { } }, []) + if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) { + console.log("In refinement: ", inputsearch) + //setLocalMessage(inputsearch) + refine(inputsearch) + } else if (onlyResults === true) { + // Don't return anything unless refinement works + return null + } + return (
+ {onlyResults !== true ? @@ -210,8 +218,8 @@ const AppGrid = props => { }} limit={5} /> - {/*isSearchStalled ? 'My search is stalled' : ''*/} - + : null} + ) } @@ -228,29 +236,35 @@ const AppGrid = props => { var counted = 0 return ( - - {hits.map((data, index) => { - workflowDelay += 50 +
+ {onlyResults === true && hits.length > 0 ? + + Relevant Workflows + + : null} + + {hits.map((data, index) => { + workflowDelay += 50 - if (counted === 12/xs*rowHandler) { - return null - } + if (counted === 12/xs*rowHandler) { + return null + } - counted += 1 + counted += 1 - return ( - - + return ( + + {/**/} {alternativeView === true ? : } - - ) - })} - + ) + })} + +
) } @@ -331,11 +345,11 @@ const AppGrid = props => { fullWidth={true} placeholder="What apps do you want to see?" type="" - id="standard-required" + id="standard-required" margin="normal" variant="outlined" autoComplete="off" - onChange={e => setMessage(e.target.value)} + onChange={e => setMessage(e.target.value)} />
: null } - - - - Search by - - - Algolia logo - - + {onlyResults === true ? null : + + + Search by + + + Algolia logo + + + }
) } From 99d1a96c63ffc11b5b743ec68c55ca5729392986 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 17 Aug 2023 03:16:43 +0200 Subject: [PATCH 024/160] Changed from Alert to Toast to work well with mui v18 --- frontend/src/components/AppFramework.jsx | 27 ++- frontend/src/components/AppGrid.jsx | 2 +- frontend/src/components/Appsearch.jsx | 8 +- .../src/components/AuthenticationItem.jsx | 17 +- .../src/components/AuthenticationNormal.jsx | 10 +- .../src/components/AuthenticationWindow.jsx | 12 +- frontend/src/components/Branding.jsx | 4 +- frontend/src/components/CacheView.jsx | 22 +- frontend/src/components/ConfigureWorkflow.jsx | 22 +- frontend/src/components/CreatorGrid.jsx | 2 +- frontend/src/components/DocsGrid.jsx | 2 +- frontend/src/components/EditWorkflow.jsx | 2 +- frontend/src/components/Files.jsx | 34 +-- frontend/src/components/Header.jsx | 12 +- frontend/src/components/Oauth2Auth.jsx | 10 +- frontend/src/components/OrgHeader.jsx | 8 +- frontend/src/components/ParsedAction.jsx | 12 +- frontend/src/components/Priorities.jsx | 2 +- frontend/src/components/Priority.jsx | 8 +- frontend/src/components/ShuffleCodeEditor.jsx | 6 +- frontend/src/components/UsecaseSearch.jsx | 40 ++-- frontend/src/components/WelcomeForm2.jsx | 16 +- frontend/src/components/WorkflowGrid.jsx | 4 +- frontend/src/components/Workflowsearch.jsx | 2 +- frontend/src/views/Admin.jsx | 189 ++++++++-------- frontend/src/views/AngularWorkflow.jsx | 211 +++++++++--------- frontend/src/views/AppCreator.jsx | 81 +++---- frontend/src/views/Apps.jsx | 81 +++---- frontend/src/views/Dashboard.jsx | 33 +-- frontend/src/views/DashboardViews.jsx | 13 +- frontend/src/views/FrameworkWrapper.jsx | 11 +- frontend/src/views/GettingStarted.jsx | 77 +++---- frontend/src/views/SettingsPage.jsx | 21 +- frontend/src/views/UpdateAuthentication.jsx | 9 +- frontend/src/views/Welcome.jsx | 4 +- frontend/src/views/Workflows.jsx | 81 +++---- 36 files changed, 553 insertions(+), 542 deletions(-) diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index b5ab004e..aca62d61 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -1,10 +1,9 @@ import React, { useState, useEffect } from 'react'; +import theme from '../theme.jsx'; import CytoscapeComponent from 'react-cytoscapejs'; import frameworkStyle from '../frameworkStyle.jsx'; import { v4 as uuidv4 } from "uuid"; -import theme from '../theme.jsx'; -import { useAlert } from "react-alert"; import AppSearch from '../components/Appsearch.jsx'; import PaperComponent from "../components/PaperComponent.jsx" @@ -34,10 +33,10 @@ import { import * as edgehandles from "cytoscape-edgehandles"; import * as cytoscape from "cytoscape"; +import { toast } from 'react-toastify'; cytoscape.use(edgehandles); - const svgSize = "40px" const parsedDatatypeImages = { "SIEM": encodeURI(`data:image/svg+xml;utf-8,`), @@ -521,7 +520,7 @@ const AppFramework = (props) => { const scale = size === undefined ? 1 : size > 5 ? 3 : size - const alert = useAlert() + //const alert = useAlert() const handleLoadNextSuggestion = (frameworkData) => { @@ -784,16 +783,16 @@ const AppFramework = (props) => { .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { - alert.error("Failed updating: " + responseJson.reason) + toast("Failed updating: " + responseJson.reason) } else { - alert.error("Failed to update framework for your org.") + toast("Failed to update framework for your org.") } } else { - alert.info("Updated usecase.") + toast("Updated usecase.") } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); //setFrameworkLoaded(true) }) } @@ -816,13 +815,13 @@ const AppFramework = (props) => { }) .then((responseJson) => { if (responseJson.success === false) { - alert.error("Failed to activate the app") + toast("Failed to activate the app") } else { - //alert.success("App activated for your organization! Refresh the page to use the app.") + //toast("App activated for your organization! Refresh the page to use the app.") } }) .catch(error => { - //alert.error(error.toString()) + //toast(error.toString()) console.log("Activate app error: ", error.toString()) }); } @@ -852,9 +851,9 @@ const AppFramework = (props) => { .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { - alert.error("Failed updating: " + responseJson.reason) + toast("Failed updating: " + responseJson.reason) } else { - alert.error("Failed to update framework for your org.") + toast("Failed to update framework for your org.") } } @@ -863,7 +862,7 @@ const AppFramework = (props) => { //setFrameworkData(responseJson) }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); //setFrameworkLoaded(true) }) } diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index 1e6ab296..57369deb 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -74,7 +74,7 @@ const AppGrid = props => { .then(response => { if (response.success === true) { setFormMessage(response.reason) - //alert.info("Thanks for submitting!") + //toast("Thanks for submitting!") } else { setFormMessage(errorMessage) } diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index 8beb1014..5f88e99a 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -2,8 +2,8 @@ import React, { useState, useEffect } from 'react'; import ReactGA from 'react-ga4'; import theme from '../theme'; import {Link} from 'react-router-dom'; -import { useAlert } from "react-alert"; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; +import { toast } from 'react-toastify'; //import algoliasearch from 'algoliasearch/lite'; import algoliasearch from 'algoliasearch'; @@ -25,7 +25,7 @@ const Appsearch = props => { const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList} = props const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - const alert = useAlert(); + //const alert = useAlert(); const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs //const theme = useTheme(); @@ -68,12 +68,12 @@ const Appsearch = props => { if (response.status !== 200) { console.log("Status not 200 for set creator :O!"); } - alert.success("Sucessfully updated specialzed app.") + toast("Sucessfully updated specialzed app.") return response.json(); }) .then((responseJson) => { if (!responseJson.success && responseJson.reason !== undefined) { - alert.error("Failed updating user: " + responseJson.reason); + toast("Failed updating user: " + responseJson.reason); } }) .catch((error) => { diff --git a/frontend/src/components/AuthenticationItem.jsx b/frontend/src/components/AuthenticationItem.jsx index 06329a35..593eacb4 100644 --- a/frontend/src/components/AuthenticationItem.jsx +++ b/frontend/src/components/AuthenticationItem.jsx @@ -1,7 +1,8 @@ import React, { useState, useEffect } from "react"; import theme from '../theme.jsx'; -import { useAlert } from "react-alert"; +import { toast } from 'react-toastify'; + import { Tooltip, IconButton, @@ -34,7 +35,7 @@ const AuthenticationItem = (props) => { const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false); const [authenticationFields, setAuthenticationFields] = React.useState([]); - const alert = useAlert(); + //const alert = useAlert(); var bgColor = "#27292d"; if (index % 2 === 0) { bgColor = "#1f2023"; @@ -63,7 +64,7 @@ const AuthenticationItem = (props) => { } const deleteAuthentication = (data) => { - alert.info("Deleting auth " + data.label); + toast("Deleting auth " + data.label); // Just use this one? const url = globalUrl + "/api/v1/apps/authentication/" + data.id; @@ -79,13 +80,13 @@ const AuthenticationItem = (props) => { response.json().then((responseJson) => { console.log("RESP: ", responseJson); if (responseJson["success"] === false) { - alert.error("Failed deleting auth"); + toast("Failed deleting auth"); } else { // Need to wait because query in ES is too fast setTimeout(() => { getAppAuthentication(); }, 1000); - //alert.success("Successfully deleted authentication!") + //toast("Successfully deleted authentication!") } }) ) @@ -115,9 +116,9 @@ const AuthenticationItem = (props) => { .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error("Failed overwriting appauth in workflows"); + toast("Failed overwriting appauth in workflows"); } else { - alert.success("Successfully updated auth everywhere!"); + toast("Successfully updated auth everywhere!"); //setSelectedUserModalOpen(false); setTimeout(() => { getAppAuthentication(); @@ -126,7 +127,7 @@ const AuthenticationItem = (props) => { }) ) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; diff --git a/frontend/src/components/AuthenticationNormal.jsx b/frontend/src/components/AuthenticationNormal.jsx index a0d6acab..1140de10 100644 --- a/frontend/src/components/AuthenticationNormal.jsx +++ b/frontend/src/components/AuthenticationNormal.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import theme from '../theme.jsx'; import { v4 as uuidv4 } from "uuid"; - +import { toast } from 'react-toastify'; import { Button, @@ -54,7 +54,7 @@ const AuthenticationData = (props) => { }) .then((responseJson) => { if (!responseJson.success) { - alert.error("Failed to set app auth: " + responseJson.reason); + toast("Failed to set app auth: " + responseJson.reason); } else { if (getAppAuthentication !== undefined) { getAppAuthentication() @@ -65,11 +65,11 @@ const AuthenticationData = (props) => { } // Needs a refresh with the new authentication.. - //alert.success("Successfully saved new app auth") + //toast("Successfully saved new app auth") } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("New auth error: ", error.toString()); }); } @@ -146,7 +146,7 @@ const AuthenticationData = (props) => { selectedApp.authentication.parameters[key].name ] = "false"; } else { - alert.info( + toast( "Field " + selectedApp.authentication.parameters[key].name + " can't be empty" diff --git a/frontend/src/components/AuthenticationWindow.jsx b/frontend/src/components/AuthenticationWindow.jsx index 47dd9b4e..d7bcc55a 100755 --- a/frontend/src/components/AuthenticationWindow.jsx +++ b/frontend/src/components/AuthenticationWindow.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import theme from '../theme.jsx'; import { v4 as uuidv4 } from "uuid"; -import { useAlert } from "react-alert"; +import { toast } from 'react-toastify'; import { Divider, @@ -44,7 +44,7 @@ const AuthenticationData = (props) => { authFieldsOnly, } = props - const alert = useAlert() + //const alert = useAlert() let navigate = useNavigate(); const [submitSuccessful, setSubmitSuccessful] = useState(false) const [authenticationOption, setAuthenticationOptions] = React.useState({ @@ -99,9 +99,9 @@ const AuthenticationData = (props) => { .then((responseJson) => { if (!responseJson.success) { if (responseJson.reason === undefined) { - alert.error("Failed to set app auth. Are you logged in?") + toast("Failed to set app auth. Are you logged in?") } else { - alert.error("Failed to set app auth: " + responseJson.reason); + toast("Failed to set app auth: " + responseJson.reason); } } else { setSubmitSuccessful(true) @@ -115,7 +115,7 @@ const AuthenticationData = (props) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("New auth error: ", error.toString()); }); }; @@ -175,7 +175,7 @@ const AuthenticationData = (props) => { selectedApp.authentication.parameters[paramkey].name ] = "false"; } else { - alert.info( + toast( "Field " + selectedApp.authentication.parameters[paramkey].name + " can't be empty" diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index 4b44f5c1..e565a013 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -11,11 +11,11 @@ import { Card, } from "@mui/material"; -import { useAlert } from "react-alert"; +//import { useAlert const Branding = (props) => { const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; - const alert = useAlert(); + //const alert = useAlert(); const [publishingInfo, setPublishingInfo] = useState(""); // Should enable / disable org branding diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 62394423..6546885c 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -16,7 +16,7 @@ import { DialogTitle, DialogActions, } from "@mui/material"; -import { useAlert } from "react-alert"; +//import { useAlert import { Edit as EditIcon, @@ -72,7 +72,7 @@ const CacheView = (props) => { const [dataValue, setDataValue] = React.useState({}); const [editCache, setEditCache] = React.useState(false); const [show, setShow] = useState({}); - const alert = useAlert(); + //const alert = useAlert(); useEffect(() => { listOrgCache(orgId); console.log("orgid", orgId); @@ -105,7 +105,7 @@ const CacheView = (props) => { } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -148,7 +148,7 @@ const CacheView = (props) => { const deleteCache = (orgId, key) => { - alert.info("Attempting to delete Cache"); + toast("Attempting to delete Cache"); fetch(globalUrl + `/api/v1/orgs/${orgId}/cache/${key}`, { method: "DELETE", headers: { @@ -158,16 +158,16 @@ const CacheView = (props) => { }) .then((response) => { if (response.status === 200) { - alert.success("Successfully deleted Cache"); + toast("Successfully deleted Cache"); setTimeout(() => { listOrgCache(orgId); }, 1000); } else { - alert.error("Failed deleting Cache. Does it still exist?"); + toast("Failed deleting Cache. Does it still exist?"); } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -197,12 +197,12 @@ const CacheView = (props) => { }) .then((responseJson) => { setAddCache(responseJson); - alert.success("Cache Edited Successfully!"); + toast("Cache Edited Successfully!"); listOrgCache(orgId); setModalOpen(false); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -231,12 +231,12 @@ const CacheView = (props) => { }) .then((responseJson) => { setAddCache(responseJson); - alert.success("New Cache Added Successfully!"); + toast("New Cache Added Successfully!"); listOrgCache(orgId); setModalOpen(false); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index c24703fe..6c734c8e 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -112,9 +112,9 @@ const ConfigureWorkflow = (props) => { }) .then((response) => { if (response.status === 200) { - //alert.success("Successfully GOT app "+appId) + //toast("Successfully GOT app "+appId) } else { - alert.error("Failed getting app"); + toast("Failed getting app"); } return response.json(); @@ -128,7 +128,7 @@ const ConfigureWorkflow = (props) => { } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -566,7 +566,7 @@ const ConfigureWorkflow = (props) => { .then((response) => { if (response.status !== 200) { //window.location.pathname = "/search" - //alert.error("Failed to find this app. Is it public?") + //toast("Failed to find this app. Is it public?") } return response.json(); @@ -574,16 +574,16 @@ const ConfigureWorkflow = (props) => { .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { - alert.error("Failed to activate the app: "+responseJson.reason); + toast("Failed to activate the app: "+responseJson.reason); } else { - alert.error("Failed to activate the app"); + toast("Failed to activate the app"); } } else { - alert.success("App activated for your organization!"); + toast("App activated for your organization!"); } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -693,7 +693,7 @@ const ConfigureWorkflow = (props) => { if (workflow.actions !== null) { //console.log(workflow.actions) - alert.info("Setting action to version "+action.update_version) + toast("Setting action to version "+action.update_version) for (let [key,keyval] in Object.entries(workflow.actions)) { if (workflow.actions[key].app_name === action.app_name && workflow.actions[key].app_version === action.app_version) { workflow.actions[key].app_version = action.update_version @@ -851,7 +851,7 @@ const ConfigureWorkflow = (props) => { console.log("NAVIGATOR: ", navigator); const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -864,7 +864,7 @@ const ConfigureWorkflow = (props) => { /* Copy the text inside the text field */ document.execCommand("copy"); - alert.success("Copied Webhook URL"); + toast("Copied Webhook URL"); } }}> {webhook.description} diff --git a/frontend/src/components/CreatorGrid.jsx b/frontend/src/components/CreatorGrid.jsx index 9d23b439..712a2197 100644 --- a/frontend/src/components/CreatorGrid.jsx +++ b/frontend/src/components/CreatorGrid.jsx @@ -81,7 +81,7 @@ const CreatorGrid = props => { .then(response => { if (response.success === true) { setFormMessage(response.reason) - //alert.info("Thanks for submitting!") + //toast("Thanks for submitting!") } else { setFormMessage(errorMessage) } diff --git a/frontend/src/components/DocsGrid.jsx b/frontend/src/components/DocsGrid.jsx index fa194a25..947a2d76 100644 --- a/frontend/src/components/DocsGrid.jsx +++ b/frontend/src/components/DocsGrid.jsx @@ -69,7 +69,7 @@ const DocsGrid = props => { .then(response => { if (response.success === true) { setFormMessage(response.reason) - //alert.info("Thanks for submitting!") + //toast("Thanks for submitting!") } else { setFormMessage(errorMessage) } diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 38ddbcf5..9971ff3d 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -127,7 +127,7 @@ const EditWorkflow = (props) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Get workflow error: ", error.toString()); }) } diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx index 9f013ee3..2d5b68d2 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -29,7 +29,7 @@ import { Add as AddIcon, } from "@mui/icons-material"; -import { useAlert } from "react-alert"; +//import { useAlert import Dropzone from "../components/Dropzone.jsx"; import CodeEditor from "../components/ShuffleCodeEditor.jsx"; import theme from "../theme.jsx"; @@ -45,7 +45,7 @@ const Files = (props) => { const [openEditor, setOpenEditor] = React.useState(false); const [renderTextBox, setRenderTextBox] = React.useState(false); - const alert = useAlert(); + //const alert = useAlert(); const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"] var upload = ""; @@ -113,7 +113,7 @@ const Files = (props) => { } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -139,12 +139,12 @@ const Files = (props) => { }) .then((responseJson) => { if (responseJson.success) { - alert.info("Successfully deleted file " + file.name); + toast("Successfully deleted file " + file.name); } else if ( responseJson.reason !== undefined && responseJson.reason !== null ) { - alert.error("Failed to delete file: " + responseJson.reason); + toast("Failed to delete file: " + responseJson.reason); } setTimeout(() => { getFiles(); @@ -153,7 +153,7 @@ const Files = (props) => { console.log(responseJson); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -178,7 +178,7 @@ const Files = (props) => { // console.log("respdata type ->", typeof(respdata)); if (respdata.length === 0) { - alert.error("Failed getting file. Is it deleted?"); + toast("Failed getting file. Is it deleted?"); return; } return respdata @@ -189,7 +189,7 @@ const Files = (props) => { //console.log("filecontent state ",fileContent); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -210,7 +210,7 @@ const Files = (props) => { }) .then((respdata) => { if (respdata.length === 0) { - alert.error("Failed getting file. Is it deleted?"); + toast("Failed getting file. Is it deleted?"); return; } @@ -249,7 +249,7 @@ const Files = (props) => { //setSchedules(responseJson) }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -291,11 +291,11 @@ const Files = (props) => { if (responseJson.success === true) { handleFileUpload(responseJson.id, file); } else { - alert.error("Failed to upload file ", filename); + toast("Failed to upload file ", filename); } }) .catch((error) => { - alert.error("Failed to upload file ", filename); + toast("Failed to upload file ", filename); console.log(error.toString()); }); }; @@ -312,7 +312,7 @@ const Files = (props) => { .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."); + toast("File was created, but failed to upload."); return; } @@ -323,7 +323,7 @@ const Files = (props) => { //setFiles(responseJson) }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -364,7 +364,7 @@ const Files = (props) => { const files = isDropzone ? e.dataTransfer.files : e.target.files; //const reader = new FileReader(); - //alert.info("Starting fileupload") + //toast("Starting fileupload") uploadFiles(files); }; @@ -742,7 +742,7 @@ const Files = (props) => { ) { const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error( + toast( "Can only copy over HTTPS (port 3443)" ); return; @@ -758,7 +758,7 @@ const Files = (props) => { /* Copy the text inside the text field */ document.execCommand("copy"); - alert.info(file.id + " copied to clipboard"); + toast(file.id + " copied to clipboard"); } }} > diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 892334e3..e54877b2 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -42,7 +42,7 @@ import { Lightbulb as LightbulbIcon, } from "@mui/icons-material"; -import { useAlert } from "react-alert"; +//import { useAlert import SearchField from '../components/Searchfield.jsx' const hoverColor = "#f85a3e" @@ -51,7 +51,7 @@ const hoverOutColor = "#e8eaf6" const Header = props => { const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props; //const theme = useTheme(); - const alert = useAlert() + //const alert = useAlert() const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); @@ -101,7 +101,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho setNotifications([]) handleClose() } else { - alert.error("Failed dismissing notifications. Please try again later.") + toast("Failed dismissing notifications. Please try again later.") } }) .catch(error => { @@ -131,7 +131,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho console.log("NEW NOTIFICATIONS: ", newNotifications) setNotifications(newNotifications) } else { - alert.error("Failed dismissing notification. Please try again later.") + toast("Failed dismissing notification. Please try again later.") } }) .catch(error => { @@ -406,9 +406,9 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho setTimeout(() => { window.location.reload() }, 2000) - alert.success("Successfully changed active organization - refreshing!") + toast("Successfully changed active organization - refreshing!") } else { - alert.error("Failed changing org: ", responseJson.reason) + toast("Failed changing org: ", responseJson.reason) } }) .catch(error => { diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 8bccfb5a..4c37a251 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -1,7 +1,7 @@ import React, { useRef, useState, useEffect, useLayoutEffect } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; import theme from '../theme.jsx'; -import { useAlert } from "react-alert"; +//import { useAlert import { v4 as uuidv4 } from "uuid"; import { @@ -99,7 +99,7 @@ const AuthenticationOauth2 = (props) => { } = props; let navigate = useNavigate(); - const alert = useAlert() + //const alert = useAlert() //const [update, setUpdate] = React.useState("|") const [defaultConfigSet, setDefaultConfigSet] = React.useState( @@ -422,7 +422,7 @@ const AuthenticationOauth2 = (props) => { //} //while(open === true) } catch (e) { - alert.error( + toast( "Failed authentication - probably bad credentials. Try again" ); setButtonClicked(false); @@ -451,7 +451,7 @@ const AuthenticationOauth2 = (props) => { console.log("NEW AUTH: ", authenticationOption); if (authenticationOption.label.length === 0) { authenticationOption.label = `Auth for ${selectedApp.name}`; - //alert.info("Label can't be empty") + //toast("Label can't be empty") //return } @@ -479,7 +479,7 @@ const AuthenticationOauth2 = (props) => { selectedApp.authentication.parameters[key].name ] = "false"; } else { - alert.info( + toast( "Field " + selectedApp.authentication.parameters[key].name.replace("_basic", "", -1).replace("_", " ", -1) + " can't be empty" ); diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 3b5a091a..b29ed7a9 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -17,7 +17,7 @@ import { Save as SaveIcon, } from "@mui/icons-material"; -import { useAlert } from "react-alert"; +//import { useAlert const useStyles = makeStyles({ notchedOutline: { @@ -39,7 +39,7 @@ const OrgHeader = (props) => { handleEditOrg, } = props; - const alert = useAlert(); + //const alert = useAlert(); const classes = useStyles(); var upload = ""; @@ -210,13 +210,13 @@ const OrgHeader = (props) => { const invalid = ["#", ":", "."]; for (var key in invalid) { if (e.target.value.includes(invalid[key])) { - alert.error("Can't use " + invalid[key] + " in name"); + toast("Can't use " + invalid[key] + " in name"); return; } } if (e.target.value.length > 100) { - alert.error("Choose a shorter name."); + toast("Choose a shorter name."); return; } diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 10353f2f..3751dfa7 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -7,7 +7,7 @@ import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { GetParsedPaths } from "../views/Apps.jsx"; import { sortByKey } from "../views/AngularWorkflow.jsx"; import { NestedMenuItem } from "mui-nested-menu"; -import { useAlert } from "react-alert"; +//import { useAlert import { ButtonGroup, @@ -172,7 +172,7 @@ const ParsedAction = (props) => { } = props; const classes = useStyles(); - const alert = useAlert() + //const alert = useAlert() const [hideBody, setHideBody] = React.useState(true); const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false); @@ -227,9 +227,9 @@ const ParsedAction = (props) => { }) .then((response) => { if (response.status === 200) { - //alert.success("Successfully GOT app "+appId) + //toast("Successfully GOT app "+appId) } else { - alert.error("Failed getting app"); + toast("Failed getting app"); } return response.json(); @@ -290,7 +290,7 @@ const ParsedAction = (props) => { //foundparams.push(param.name) } } else { - alert.error("Couldn't find action " + selectedAction.name); + toast("Couldn't find action " + selectedAction.name); } selectedAction.errors = []; @@ -306,7 +306,7 @@ const ParsedAction = (props) => { } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index 86215b6f..f003540c 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -12,7 +12,7 @@ import { } from "@mui/material"; import Priority from "../components/Priority.jsx"; -import { useAlert } from "react-alert"; +//import { useAlert const Priorities = (props) => { const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, } = props; diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index a4f93729..3a23f15f 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -17,7 +17,7 @@ import { AutoFixHigh as AutoFixHighIcon, ArrowForward as ArrowForwardIcon, } from '@mui/icons-material'; -import { useAlert } from "react-alert"; +//import { useAlert const Priority = (props) => { const { globalUrl, userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, } = props; @@ -56,14 +56,14 @@ const Priority = (props) => { } } else { if (responseJson.success === false && responseJson.reason !== undefined) { - alert.error("Failed change recommendation: ", responseJson.reason) + toast("Failed change recommendation: ", responseJson.reason) } else { - alert.error("Failed change recommendation"); + toast("Failed change recommendation"); } } }) .catch((error) => { - alert.info("Failed dismissing alert. Please contact support@shuffler.io if this persists."); + toast("Failed dismissing alert. Please contact support@shuffler.io if this persists."); }); } diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 21293d7c..4b9dd48a 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -861,12 +861,12 @@ const CodeEditor = (props) => { var newResult = {} if (responseJson.success === true && responseJson.result !== null && responseJson.result !== undefined && responseJson.result.length > 0) { const result = responseJson.result.slice(0, 50)+"..." - //alert.info("SUCCESS: "+result) + //toast("SUCCESS: "+result) const validate = validateJson(responseJson.result) newResult = validate } else if (responseJson.success === false && responseJson.reason !== undefined && responseJson.reason !== null) { - alert.error(responseJson.reason) + toast(responseJson.reason) newResult = {"valid": false, "result": responseJson.reason} } else if (responseJson.success === true) { newResult = {"valid": false, "result": "Couldn't finish execution. Please fill all the required fields, and retry the execution."} @@ -882,7 +882,7 @@ const CodeEditor = (props) => { setExecuting(false) }) .catch(error => { - //alert.error("Execution error: "+error.toString()) + //toast("Execution error: "+error.toString()) console.log("error: ", error) setExecuting(false) }) diff --git a/frontend/src/components/UsecaseSearch.jsx b/frontend/src/components/UsecaseSearch.jsx index ba8a98a1..6d0f2eef 100644 --- a/frontend/src/components/UsecaseSearch.jsx +++ b/frontend/src/components/UsecaseSearch.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import theme from '../theme.jsx'; import { useNavigate, Link } from "react-router-dom"; -import { useAlert } from "react-alert"; +//import { useAlert import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; import PaperComponent from "../components/PaperComponent.jsx" import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; @@ -349,14 +349,14 @@ const UsecaseSearch = (props) => { const [firstRequest, setFirstRequest] = React.useState(true); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - const alert = useAlert() + //const alert = useAlert() useEffect(() => { // if (firstRequest !== true && workflow.id !== undefined && autotry === true && setUsecaseSearch !== undefined && authenticationModalOpen === false && configureWorkflowModalOpen === false) { // if (autotry === true && configureWorkflowModalOpen === false && workflow.id !== undefined && setUsecaseSearch !== undefined) { console.log("Close it?") - alert.info("Workflow successfully added! Add more apps, and we will suggest more workflows") + toast("Workflow successfully added! Add more apps, and we will suggest more workflows") if (setCloseWindow !== undefined) { setCloseWindow(true) @@ -793,7 +793,7 @@ const UsecaseSearch = (props) => { console.log("Deleted workflow") }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Delete workflow error: ", error.toString()); }) } @@ -822,7 +822,7 @@ const UsecaseSearch = (props) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Get workflows error: ", error.toString()); }) } @@ -894,9 +894,9 @@ const UsecaseSearch = (props) => { .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { - alert.error("Error setting workflow: ", responseJson.reason) + toast("Error setting workflow: ", responseJson.reason) } else { - alert.error("Error setting workflow.") + toast("Error setting workflow.") } return @@ -905,7 +905,7 @@ const UsecaseSearch = (props) => { return responseJson; }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); } @@ -930,7 +930,7 @@ const UsecaseSearch = (props) => { if (responseJson.success === false) { if (responseJson.reason !== null && responseJson.reason !== undefined) { - //alert.error(responseJson.reason) + //toast(responseJson.reason) } if (responseJson.source === "") { @@ -1009,13 +1009,13 @@ const UsecaseSearch = (props) => { responseJson.status, ).then((response) => { if (response !== undefined) { - alert.success("Successfully generated " + responseJson.name); + toast("Successfully generated " + responseJson.name); } }); } }) .catch((error) => { - alert.error("Generate error: " + error.toString()); + toast("Generate error: " + error.toString()); }) @@ -1057,7 +1057,7 @@ const UsecaseSearch = (props) => { .catch((error) => { setIsUploading(false) console.log("Merge err: ", error.toString()) - //alert.error("Err: " + error.toString()); + //toast("Err: " + error.toString()); }); } @@ -1207,7 +1207,7 @@ const UsecaseSearch = (props) => { } if (changed) { - //alert.error("Errors were found. Click them to sort sort them out or go to the next usecase.") + //toast("Errors were found. Click them to sort sort them out or go to the next usecase.") //setUpdate(Math.random()) //setIsUploading(false) @@ -1268,13 +1268,13 @@ const UsecaseSearch = (props) => { }) .then((responseJson) => { if (responseJson.success === false) { - alert.error("Failed to activate the app") + toast("Failed to activate the app") } else { - //alert.success("App activated for your organization! Refresh the page to use the app.") + //toast("App activated for your organization! Refresh the page to use the app.") } }) .catch(error => { - //alert.error(error.toString()) + //toast(error.toString()) console.log("Activate app error: ", error.toString()) }); } @@ -1304,9 +1304,9 @@ const UsecaseSearch = (props) => { .then((responseJson) => { if (responseJson.success === false) { if (responseJson.reason !== undefined) { - alert.error("Failed updating default app: " + responseJson.reason) + toast("Failed updating default app: " + responseJson.reason) } else { - alert.error("Failed to update framework for your org.") + toast("Failed to update framework for your org.") } } else { @@ -1319,7 +1319,7 @@ const UsecaseSearch = (props) => { //setFrameworkData(responseJson) }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); //setFrameworkLoaded(true) }) } @@ -1516,7 +1516,7 @@ const UsecaseSearch = (props) => { return (
{ if (subdata.disabled === true) { - //alert.info("Usecase not available yet.") + //toast("Usecase not available yet.") return } diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index 5d887851..83d43e2b 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -38,7 +38,7 @@ import { Chip, ButtonGroup, } from "@mui/material"; -import { useAlert } from "react-alert"; +//import { useAlert import { useNavigate, Link } from "react-router-dom"; import WorkflowSearch from '../components/Workflowsearch.jsx'; @@ -134,7 +134,7 @@ const WelcomeForm = (props) => { const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - const alert = useAlert(); + //const alert = useAlert(); let navigate = useNavigate(); const onNodeSelect = (label) => { @@ -262,16 +262,16 @@ const WelcomeForm = (props) => { response.json().then((responseJson) => { if (responseJson["success"] === false) { console.log("Update user success") - //alert.error("Failed updating org: ", responseJson.reason); + //toast("Failed updating org: ", responseJson.reason); } else { console.log("Update success!") - //alert.success("Successfully edited org!"); + //toast("Successfully edited org!"); } }) ) .catch((error) => { console.log("Update err: ", error.toString()) - //alert.error("Err: " + error.toString()); + //toast("Err: " + error.toString()); }); } @@ -308,15 +308,15 @@ const WelcomeForm = (props) => { response.json().then((responseJson) => { if (responseJson["success"] === false) { console.log("Update of org failed") - //alert.error("Failed updating org: ", responseJson.reason); + //toast("Failed updating org: ", responseJson.reason); } else { - //alert.success("Successfully edited org!"); + //toast("Successfully edited org!"); } }) ) .catch((error) => { console.log("Update err: ", error.toString()) - //alert.error("Err: " + error.toString()); + //toast("Err: " + error.toString()); }); } diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index fb19e806..e721dc3f 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -69,7 +69,7 @@ const AppGrid = props => { .then(response => { if (response.success === true) { setFormMessage(response.reason) - //alert.info("Thanks for submitting!") + //toast("Thanks for submitting!") } else { setFormMessage(errorMessage) } @@ -158,7 +158,7 @@ const AppGrid = props => { } }) .catch((error) => { - //alert.error("ERROR: " + error.toString()); + //toast("ERROR: " + error.toString()); console.log("ERROR: " + error.toString()); }); }; diff --git a/frontend/src/components/Workflowsearch.jsx b/frontend/src/components/Workflowsearch.jsx index 267face5..b8f4c022 100644 --- a/frontend/src/components/Workflowsearch.jsx +++ b/frontend/src/components/Workflowsearch.jsx @@ -53,7 +53,7 @@ const WorkflowSearch = props => { .then(response => { if (response.success === true) { setFormMessage(response.reason) - //alert.info("Thanks for submitting!") + //toast("Thanks for submitting!") } else { setFormMessage(errorMessage) } diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 0befde05..95a4f042 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -75,7 +75,8 @@ import { FmdGood as FmdGoodIcon, } from "@mui/icons-material"; -import { useAlert } from "react-alert"; +//import { useAlert +import { ToastContainer, toast } from "react-toastify" import Dropzone from "../components/Dropzone.jsx"; import HandlePaymentNew from "../views/HandlePaymentNew.jsx"; import OrgHeader from "../components/OrgHeader.jsx"; @@ -216,13 +217,13 @@ const Admin = (props) => { .then((responseJson) => { //console.log("RESPONSE: ", responseJson) if (responseJson.success === true) { - //alert.info(responseJson.reason) + //toast(responseJson.reason) setImage2FA(responseJson.reason); setSecret2FA(responseJson.extra); } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -256,7 +257,7 @@ const Admin = (props) => { //} }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -287,7 +288,7 @@ const Admin = (props) => { ] */ - const alert = useAlert(); + //const alert = useAlert(); const handleStatusChange = (event) => { const { value } = event.target; console.log("value: ", value) @@ -465,7 +466,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user } const deleteAuthentication = (data) => { - alert.info("Deleting auth " + data.label); + toast("Deleting auth " + data.label); // Just use this one? const url = globalUrl + "/api/v1/apps/authentication/" + data.id; @@ -480,13 +481,13 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error("Failed deleting auth"); + toast("Failed deleting auth"); } else { // Need to wait because query in ES is too fast setTimeout(() => { getAppAuthentication(); }, 1000); - //alert.success("Successfully deleted authentication!") + //toast("Successfully deleted authentication!") } }) ) @@ -518,12 +519,12 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user response.json().then((responseJson) => { console.log("RESP: ", responseJson); if (responseJson["success"] === false) { - alert.error("Failed stopping schedule"); + toast("Failed stopping schedule"); } else { setTimeout(() => { getSchedules(); }, 1500); - //alert.success("Successfully stopped schedule!") + //toast("Successfully stopped schedule!") } }) ) @@ -534,7 +535,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user if (userdata.support === true && selectedOrganization.id !== "" && selectedOrganization.id !== undefined && selectedOrganization.id !== null && selectedOrganization.id !== userdata.active_org.id) { - alert.info("Refreshing window to fix org support access") + toast("Refreshing window to fix org support access") window.location.reload() return null } @@ -559,15 +560,15 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => { if (response.status === 200) { } else { - //alert.info("Wrong code sent.") - //alert.info("Wrong code sent. Please try again.") + //toast("Wrong code sent.") + //toast("Wrong code sent. Please try again.") } return response.json(); }) .then((responseJson) => { if (responseJson.success === true) { - alert.info("Successfully enabled 2fa"); + toast("Successfully enabled 2fa"); setTimeout(() => { getUsers(); @@ -579,19 +580,19 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user setSelectedUserModalOpen(false); }, 1000); } else { - alert.info("Wrong code sent. Please try again."); - //alert.error("Failed setting 2fa: ", responseJson.reason) + toast("Wrong code sent. Please try again."); + //toast("Failed setting 2fa: ", responseJson.reason) } }) .catch((error) => { - alert.info("Wrong code sent. Please try again."); - //alert.error("Err: " + error.toString()) + toast("Wrong code sent. Please try again."); + //toast("Err: " + error.toString()) }); }; const handleStopOrgSync = (org_id) => { if (org_id === undefined || org_id === null) { - alert.error("Couldn't get org " + org_id); + toast("Couldn't get org " + org_id); return; } @@ -612,10 +613,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => { if (response.status === 200) { console.log("Cloud sync success?"); - alert.success("Successfully stopped cloud sync"); + toast("Successfully stopped cloud sync"); } else { console.log("Cloud sync fail?"); - alert.error( + toast( "Failed stopping sync. Try again, and contact support if this persists." ); } @@ -628,7 +629,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }, 1000); }) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; @@ -672,16 +673,16 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user responseJson.reason !== undefined ) { setOrgSyncResponse(responseJson.reason); - alert.error("Failed to handle sync: " + responseJson.reason); + toast("Failed to handle sync: " + responseJson.reason); } else if (!responseJson.success) { - alert.error("Failed to handle sync."); + toast("Failed to handle sync."); } else { getOrgs(); if (disableSync) { - alert.success("Successfully disabled sync!"); + toast("Successfully disabled sync!"); setOrgSyncResponse("Successfully disabled syncronization"); } else { - alert.success("Cloud Syncronization successfully set up!"); + toast("Cloud Syncronization successfully set up!"); setOrgSyncResponse( "Successfully started syncronization. Cloud features you now have access to can be seen below." ); @@ -696,7 +697,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) .catch((error) => { setLoading(false); - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; @@ -718,16 +719,16 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error("Failed changing authentication"); + toast("Failed changing authentication"); } else { - //alert.success("Successfully password!") + //toast("Successfully password!") setSelectedUserModalOpen(false); getAppAuthentication(); } }) ) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; @@ -766,16 +767,16 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error("Failed updating org: ", responseJson.reason); + toast("Failed updating org: ", responseJson.reason); } else { if (lead_info === undefined || lead_info === null || lead_info === []) { - alert.success("Successfully edited org!"); + toast("Successfully edited org!"); } } }) ) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; @@ -800,9 +801,9 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error("Failed overwriting appauth in workflows"); + toast("Failed overwriting appauth in workflows"); } else { - alert.success("Successfully updated auth everywhere!"); + toast("Successfully updated auth everywhere!"); setSelectedUserModalOpen(false); setTimeout(() => { getAppAuthentication(); @@ -811,7 +812,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) ) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; @@ -835,12 +836,12 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user response.json().then((responseJson) => { if (responseJson["success"] === false) { if (responseJson.reason !== undefined) { - alert.error(responseJson.reason); + toast(responseJson.reason); } else { - alert.error("Failed creating suborg. Please try again"); + toast("Failed creating suborg. Please try again"); } } else { - alert.success( + toast( "Successfully created suborg. Reloading in 3 seconds!" ); setSelectedUserModalOpen(false); @@ -855,7 +856,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) ) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; @@ -878,18 +879,18 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user response.json().then((responseJson) => { if (responseJson["success"] === false) { if (responseJson.reason !== undefined) { - alert.error(responseJson.reason); + toast(responseJson.reason); } else { - alert.error("Failed setting new password"); + toast("Failed setting new password"); } } else { - alert.success("Successfully updated password!"); + toast("Successfully updated password!"); setSelectedUserModalOpen(false); } }) ) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; @@ -914,9 +915,9 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) .then((responseJson) => { if (!responseJson.success && responseJson.reason !== undefined) { - alert.error("Failed to deactivate user: " + responseJson.reason); + toast("Failed to deactivate user: " + responseJson.reason); } else { - alert.success("Changed activation for user " + data.id); + toast("Changed activation for user " + data.id); } }) @@ -939,7 +940,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user } if (orgId.length === 0) { - alert.error("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); + toast("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); return; } @@ -960,7 +961,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) .then((responseJson) => { if (responseJson["success"] === false) { - alert.error("Failed getting your org. If this persists, please contact support."); + toast("Failed getting your org. If this persists, please contact support."); } else { if ( responseJson.sync_features === undefined || @@ -1032,7 +1033,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) .catch((error) => { console.log("Error getting org: ", error); - alert.error("Error getting current organization"); + toast("Error getting current organization"); }); }; @@ -1061,7 +1062,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user response.json().then((responseJson) => { if (responseJson["success"] === false) { setLoginInfo("Error: " + responseJson.reason); - alert.error("Failed to send email (2). Please try again and contact support if this persists.") + toast("Failed to send email (2). Please try again and contact support if this persists.") } else { setLoginInfo(""); setModalOpen(false); @@ -1069,13 +1070,13 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user getUsers(); }, 1000); - alert.info("Invite sent! They will show up in the list when they have accepted the invite.") + toast("Invite sent! They will show up in the list when they have accepted the invite.") } }) ) .catch((error) => { console.log("Error in userdata: ", error); - alert.error("Failed to send email. Please try again and contact support if this persists.") + toast("Failed to send email. Please try again and contact support if this persists.") }); }; @@ -1117,12 +1118,12 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user // Horrible frontend fix for environments const setDefaultEnvironment = (environment) => { // FIXME - add more checks to this - alert.info("Setting default env to " + environment.name); + toast("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"); + toast("Can't set archived to default"); return; } @@ -1150,7 +1151,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error(responseJson.reason); + toast(responseJson.reason); setTimeout(() => { getEnvironments(); }, 1500); @@ -1181,7 +1182,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error(responseJson.reason); + toast(responseJson.reason); getEnvironments(); } else { setLoginInfo(""); @@ -1196,7 +1197,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }; const rerunCloudWorkflows = (environment) => { - alert.info("Starting execution reruns. This can run in the background.") + toast("Starting execution reruns. This can run in the background.") fetch( `${globalUrl}/api/v1/environments/${environment.id}/rerun`, { @@ -1209,8 +1210,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user console.log("Status not 200 for apps :O!"); return; } else { - alert.error(response.reason); - //alert.info("Aborted all dangling workflows"); + toast(response.reason); + //toast("Aborted all dangling workflows"); } return response.json(); @@ -1221,7 +1222,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user //setFiles(responseJson) }) .catch((error) => { - //alert.error(error.toString()) + //toast(error.toString()) }); }; @@ -1238,10 +1239,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => { if (response.status !== 200) { console.log("Status not 200 for apps :O!"); - alert.error("Failed aborting dangling workflows"); + toast("Failed aborting dangling workflows"); return; } else { - alert.info("Aborted all dangling workflows"); + toast("Aborted all dangling workflows"); } return response.json(); @@ -1252,7 +1253,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user //setFiles(responseJson) }) .catch((error) => { - //alert.error(error.toString()) + //toast(error.toString()) }); }; @@ -1260,17 +1261,17 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user // FIXME - add some check here ROFL //const name = environment.name - //alert.info("Modifying environment " + name) + //toast("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") + // toast("Can't modify the default environment") // return // } // if (environments[key].type === "cloud" && !environments[key].archived) { - // alert.error("Can't modify cloud environments") + // toast("Can't modify cloud environments") // return // } @@ -1281,17 +1282,17 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user //} const id = environment.id; - //alert.info("Modifying environment " + environment.Name) + //toast("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"); + toast("Can't modify the default environment"); return; } if (environments[key].type === "cloud" && !environments[key].archived) { - alert.error("Can't modify cloud environments"); + toast("Can't modify cloud environments"); return; } @@ -1314,7 +1315,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error(responseJson.reason); + toast(responseJson.reason); getEnvironments(); } else { setLoginInfo(""); @@ -1387,7 +1388,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user setSchedules(responseJson); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -1414,11 +1415,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user //console.log(responseJson) setAuthentication(responseJson.data); } else { - alert.error("Failed getting authentications"); + toast("Failed getting authentications"); } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -1443,7 +1444,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user setEnvironments(responseJson); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -1471,7 +1472,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user setOrganizations(responseJson); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -1497,7 +1498,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user setUsers(responseJson); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -1672,10 +1673,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) .then((responseJson) => { if (!responseJson.success && responseJson.reason !== undefined) { - alert.error("Failed setting user: " + responseJson.reason); + toast("Failed setting user: " + responseJson.reason); } else { - //alert.success("Set the user field " + field + " to " + value); - alert.success("Successfully updated user field " + field) + //toast("Set the user field " + field + " to " + value); + toast("Successfully updated user field " + field) if (field !== "suborgs") { setSelectedUserModalOpen(false); @@ -1712,9 +1713,9 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user .then((responseJson) => { console.log("RESP: ", responseJson); if (!responseJson.success && responseJson.reason !== undefined) { - alert.error("Failed getting new: " + responseJson.reason); + toast("Failed getting new: " + responseJson.reason); } else { - alert.success("Got new API key"); + toast("Got new API key"); } }) .catch((error) => { @@ -1808,9 +1809,9 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user } if (error) { - alert.error("All fields must have a new value"); + toast("All fields must have a new value"); } else { - alert.success("Saving new version of this authentication"); + toast("Saving new version of this authentication"); selectedAuthentication.fields = authenticationFields; saveAuthentication(selectedAuthentication); setSelectedAuthentication({}); @@ -1827,7 +1828,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user const handleOrgEditChange = (event) => { if (userdata.id === selectedUser.id) { - alert.info("Can't remove orgs from yourself"); + toast("Can't remove orgs from yourself"); return; } @@ -2424,7 +2425,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user if (copyText !== null && copyText !== undefined) { const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -2438,7 +2439,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user /* Copy the text inside the text field */ document.execCommand("copy"); - alert.info(org_id + " copied to clipboard"); + toast(org_id + " copied to clipboard"); } }} > @@ -3115,7 +3116,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user ) { const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error( + toast( "Can only copy over HTTPS (port 3443)" ); return; @@ -3131,7 +3132,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user /* Copy the text inside the text field */ document.execCommand("copy"); - alert.info("Apikey copied to clipboard"); + toast("Apikey copied to clipboard"); } }} > @@ -3786,14 +3787,14 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user } } else { if (responseJson.success === false && responseJson.reason !== undefined) { - alert.error("Failed change recommendation: ", responseJson.reason) + toast("Failed change recommendation: ", responseJson.reason) } else { - alert.error("Failed change recommendation"); + toast("Failed change recommendation"); } } }) .catch((error) => { - alert.info("Failed dismissing alert. Please contact support@shuffler.io if this persists."); + toast("Failed dismissing alert. Please contact support@shuffler.io if this persists."); }); } @@ -3953,7 +3954,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user disabled={environment.Type === "cloud"} onClick={() => { if (environment.Type === "cloud") { - alert.info("No Orborus necessary for environment cloud. Create and use a different environment to run executions on-premises.") + toast("No Orborus necessary for environment cloud. Create and use a different environment to run executions on-premises.") return } @@ -3964,7 +3965,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user if (copyText !== null && copyText !== undefined) { const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -3978,7 +3979,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user /* Copy the text inside the text field */ document.execCommand("copy"); - alert.info("Orborus command copied to clipboard"); + toast("Orborus command copied to clipboard"); } }} > diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 5378d297..bc2191aa 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -12,7 +12,8 @@ import { useBeforeunload } from "react-beforeunload"; import ReactJson from "react-json-view"; import { NestedMenuItem } from 'mui-nested-menu'; import ReactMarkdown from "react-markdown"; -import { useAlert } from "react-alert"; +//import { useAlert +import { ToastContainer, toast } from "react-toastify" import { isMobile } from "react-device-detect" import aa from 'search-insights' import Drift from "react-driftjs"; @@ -384,7 +385,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52 const AngularWorkflow = (defaultprops) => { const { globalUrl, isLoggedIn, isLoaded, userdata, data_id } = defaultprops; const referenceUrl = globalUrl + "/api/v1/hooks/"; - const alert = useAlert() + //const alert = useAlert() let navigate = useNavigate(); const params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) @@ -584,9 +585,9 @@ const AngularWorkflow = (defaultprops) => { }) .then((response) => { if (response.status === 200) { - //alert.success("Successfully GOT app "+appId) + //toast("Successfully GOT app "+appId) } else { - //alert.error("Failed getting app"); + //toast("Failed getting app"); } return response.json(); @@ -604,7 +605,7 @@ const AngularWorkflow = (defaultprops) => { }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -644,7 +645,7 @@ const AngularWorkflow = (defaultprops) => { setListCache(responseJson); }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -746,7 +747,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Workflow error: ", error.toString()) }); }; @@ -893,17 +894,17 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { - alert.error("Failed to set app auth: " + responseJson.reason); + toast("Failed to set app auth: " + responseJson.reason); } else { getAppAuthentication(true, false); setAuthenticationModalOpen(false); // Needs a refresh with the new authentication.. - //alert.success("Successfully saved new app auth") + //toast("Successfully saved new app auth") } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("New auth error: ", error.toString()); }); }; @@ -992,7 +993,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Get execution error: ", error.toString()); }); }; @@ -1046,7 +1047,7 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Abort error: ", error.toString()); }); }; @@ -1159,7 +1160,7 @@ const AngularWorkflow = (defaultprops) => { if (!visited.includes(label)) { //if (item.action.result !== undefined && item.action.result !== null && !item.action.result.includes("failed condition")) { - // alert.error("Error for " + item.action.label + " with result " + item.result); + // toast("Error for " + item.action.label + " with result " + item.result); //} visited.push(label); setVisited(visited); @@ -1287,7 +1288,7 @@ const AngularWorkflow = (defaultprops) => { }) .catch((error) => { console.log("Stream send error: ", error.toString()) - //alert.error(error.toString()); + //toast(error.toString()); }) } @@ -1458,7 +1459,7 @@ const AngularWorkflow = (defaultprops) => { newComments.push(curworkflowComment); } else { - alert.info("No handler for type: " + type); + toast("No handler for type: " + type); } } } @@ -1519,9 +1520,9 @@ const AngularWorkflow = (defaultprops) => { if (!responseJson.success) { console.log(responseJson); if (responseJson.reason !== undefined && responseJson.reason !== null) { - alert.error("Failed to save: " + responseJson.reason); + toast("Failed to save: " + responseJson.reason); } else { - alert.error("Failed to save. Please contact your support@shuffler.io or your local admin if this is unexpected.") + toast("Failed to save. Please contact your support@shuffler.io or your local admin if this is unexpected.") } } else { @@ -1570,7 +1571,7 @@ const AngularWorkflow = (defaultprops) => { }) .catch((error) => { setSavingState(0); - //alert.error(error.toString()); + //toast(error.toString()); console.log("Save workflow error: ", error.toString()); }); @@ -1615,12 +1616,12 @@ const AngularWorkflow = (defaultprops) => { } if (workflow.public) { - alert.info("Save it to get a new version"); + toast("Save it to get a new version"); } var returncheck = monitorUpdates(); if (!returncheck) { - alert.error("No startnode set."); + toast("No startnode set."); return; } @@ -1656,7 +1657,7 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { - alert.error("Failed to start: " + responseJson.reason); + toast("Failed to start: " + responseJson.reason); setExecutionRunning(false); setExecutionRequestStarted(false); stop(); @@ -1676,7 +1677,7 @@ const AngularWorkflow = (defaultprops) => { responseJson.authorization === "" || responseJson.authorization === undefined ) { - alert.error("Something went wrong during execution startup"); + toast("Something went wrong during execution startup"); console.log("BAD RESPONSE FOR EXECUTION: ", responseJson); setExecutionRunning(false); setExecutionRequestStarted(false); @@ -1698,7 +1699,7 @@ const AngularWorkflow = (defaultprops) => { start(); }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); setExecutionRequestStarted(false) console.log("Execute workflow err: ", error.toString()); }); @@ -1809,16 +1810,16 @@ const AngularWorkflow = (defaultprops) => { setSelectedAction(selectedAction); setWorkflow(workflow); saveWorkflow(workflow); - alert.info("Added and updated authentication!"); + toast("Added and updated authentication!"); shouldClose = true } else { console.log("Closing auth modal? FAIL") - alert.error("Failed to find new authentication. See details in Oauth2 popup window where auth was attempted."); + toast("Failed to find new authentication. See details in Oauth2 popup window where auth was attempted."); shouldClose = false } } else { - alert.info("No authentication to update"); + toast("No authentication to update"); } } else { shouldClose = true @@ -1835,7 +1836,7 @@ const AngularWorkflow = (defaultprops) => { }) .catch((error) => { setAppAuthentication([]); - //alert.error("Auth loading error: " + error.toString()); + //toast("Auth loading error: " + error.toString()); console.log("AppAuth error: " + error.toString()); }); }; @@ -1867,7 +1868,7 @@ const AngularWorkflow = (defaultprops) => { setFilteredApps(pretend_apps) setPrioritizedApps(pretend_apps); - alert.error("Something went wrong while loading apps. Please refresh the window to try again.") + toast("Something went wrong while loading apps. Please refresh the window to try again.") return } @@ -1930,7 +1931,7 @@ const AngularWorkflow = (defaultprops) => { .catch((error) => { console.log("App loading error: " + error.toString()); setAppsLoaded(true) - //alert.error("App loading error: "+error.toString()); + //toast("App loading error: "+error.toString()); }); }; @@ -2008,7 +2009,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Error loading files: ", error) }); }; @@ -2027,9 +2028,9 @@ const AngularWorkflow = (defaultprops) => { console.log("Status not 200 for workflows :O!"); if (response.status >= 500) { - alert.info("Something went wrong while loading the workflow. Please reload.") + toast("Something went wrong while loading the workflow. Please reload.") } else { - alert.info("You don't access to this workflow or loading failed.") + toast("You don't access to this workflow or loading failed.") window.location.pathname = "/workflows"; } } @@ -2064,7 +2065,7 @@ const AngularWorkflow = (defaultprops) => { fetchRecommendations(responseJson) if (responseJson.public) { - //alert.info("This workflow is public. Save the workflow to use it in your organization."); + //toast("This workflow is public. Save the workflow to use it in your organization."); setAppAuthentication([]) console.log("RESP: ", responseJson) @@ -2280,7 +2281,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Get workflows error: ", error.toString()); }); }; @@ -2411,7 +2412,7 @@ const AngularWorkflow = (defaultprops) => { event.target.data("type") !== "COMMENT" && event.target.data().decorator ) { - alert.info("This edge can't be edited."); + toast("This edge can't be edited."); } else { //console.log("DATA: ", event.target.data()) const destinationId = event.target.data("target"); @@ -2423,7 +2424,7 @@ const AngularWorkflow = (defaultprops) => { curaction.app_name === "Shuffle Tools" && curaction.name === "router" ) { - alert.info("Router action can't have incoming conditions"); + toast("Router action can't have incoming conditions"); event.target.unselect(); return; } @@ -2455,7 +2456,7 @@ const AngularWorkflow = (defaultprops) => { if (nodedata.type === "TRIGGER" && (nodedata.app_name === "Shuffle Workflow" || nodedata.app_name === "User Input")) { if (nodedata.parameters === null) { - alert.error("Set a workflow first"); + toast("Set a workflow first"); return; } @@ -3045,7 +3046,7 @@ const AngularWorkflow = (defaultprops) => { curaction = data } else { if (workflow.public !== true) { - alert.error("Action not found. Please remake it."); + toast("Action not found. Please remake it."); } event.target.remove(); @@ -3103,7 +3104,7 @@ const AngularWorkflow = (defaultprops) => { if (!curapp || curapp === undefined) { console.log("APPS - couldn't find it: ", newapps) - //alert.error(`App ${curaction.app_name}:${curaction.app_version} not found. Is it activated?`); + //toast(`App ${curaction.app_name}:${curaction.app_version} not found. Is it activated?`); const tmpapp = { name: curaction.app_name, @@ -3295,7 +3296,7 @@ const AngularWorkflow = (defaultprops) => { // workflow.actions.push(data) // curaction = data //} else { - // alert.error("Action not found. Please remake it."); + // toast("Action not found. Please remake it."); // event.target.remove(); // return; //} @@ -3329,7 +3330,7 @@ const AngularWorkflow = (defaultprops) => { } else if (data.type === "COMMENT") { setSelectedComment(data); } else { - alert.error("Can't handle node type " + data.type); + toast("Can't handle node type " + data.type); return; } @@ -3383,16 +3384,16 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (responseJson.success === false) { - alert.error("Failed to auto-activate the app. Go to /apps and activate it.") + toast("Failed to auto-activate the app. Go to /apps and activate it.") } else { if (refresh === true) { - //alert.success("App activated for your organization! Refresh the page to use the app.") + //toast("App activated for your organization! Refresh the page to use the app.") getApps() } } }) .catch(error => { - //alert.error(error.toString()) + //toast(error.toString()) console.log("Activate app error: ", error.toString()) }); } @@ -3707,7 +3708,7 @@ const AngularWorkflow = (defaultprops) => { event.target.remove() //console.log("Found branch already!") - alert.info("Triggers can have exactly one target node") + toast("Triggers can have exactly one target node") return @@ -3740,7 +3741,7 @@ const AngularWorkflow = (defaultprops) => { workflow.triggers[targetnode].app_name === "Shuffle Workflow" ) { } else { - alert.error("Can't have triggers as target of branch"); + toast("Can't have triggers as target of branch"); event.target.remove(); } } @@ -3793,7 +3794,7 @@ const AngularWorkflow = (defaultprops) => { workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target ) { - alert.error("A branch in the opposite direction already exists"); + toast("A branch in the opposite direction already exists"); event.target.remove(); found = true; break; @@ -3803,7 +3804,7 @@ const AngularWorkflow = (defaultprops) => { ) { console.log(edge.source); - //alert.error("That branch already exists"); + //toast("That branch already exists"); event.target.remove(); found = true; @@ -3814,7 +3815,7 @@ const AngularWorkflow = (defaultprops) => { ); if (targetnode === -1) { if (targetnode.type !== "TRIGGER") { - alert.error("Can't make arrow to starting node"); + toast("Can't make arrow to starting node"); event.target.remove(); break; } @@ -3829,7 +3830,7 @@ const AngularWorkflow = (defaultprops) => { // console.log("Destination: ", edge.target) // console.log("CHECK SOURCE IF ITS A TRIGGER: ", targetnode) // if (targetnode !== -1) { - // alert.error("Triggers can only target one target (startnode)") + // toast("Triggers can only target one target (startnode)") // event.target.remove() // found = true // break @@ -3844,7 +3845,7 @@ const AngularWorkflow = (defaultprops) => { console.log("TARGETNODE: ", targetnode) if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow") { } else { - alert.error("Can't have triggers as target of branch") + toast("Can't have triggers as target of branch") event.target.remove() found = true break @@ -3937,7 +3938,7 @@ const AngularWorkflow = (defaultprops) => { /* var curaction = workflow.actions.find((a) => a.id === nodedata.id); if (curaction === null || curaction === undefined) { - alert.error("Node not found. Please remake it.") + toast("Node not found. Please remake it.") event.target.remove(); } */ @@ -4016,7 +4017,7 @@ const AngularWorkflow = (defaultprops) => { setWorkflow(workflow); } else if (nodedata.type === "TRIGGER") { if (nodedata.is_valid === false) { - alert.info("This trigger is not available to you"); + toast("This trigger is not available to you"); node.remove(); return; } @@ -4101,7 +4102,7 @@ const AngularWorkflow = (defaultprops) => { data: newdata, }) - alert.error("You must STOP the trigger before deleting its branches") + toast("You must STOP the trigger before deleting its branches") } catch (e) { console.log("Failed re-adding edge: ", e) } @@ -4258,7 +4259,7 @@ const AngularWorkflow = (defaultprops) => { if (copyText !== null && copyText !== undefined) { const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -4266,7 +4267,7 @@ const AngularWorkflow = (defaultprops) => { copyText.select(); copyText.setSelectionRange(0, 99999); /* For mobile devices */ document.execCommand("copy"); - alert.success(`Copied ${cydata.length} element(s)`); + toast(`Copied ${cydata.length} element(s)`); } } } @@ -4279,7 +4280,7 @@ const AngularWorkflow = (defaultprops) => { /* const clipboard = navigator.clipboard if (clipboard === undefined || window === undefined || window === null) { - alert.error("Can only use cliboard over HTTPS (port 3443)") + toast("Can only use cliboard over HTTPS (port 3443)") return } @@ -4387,7 +4388,7 @@ const AngularWorkflow = (defaultprops) => { } } catch (e) { console.log("Error pasting: ", e); - //alert.info("Failed parsing clipboard: ", e) + //toast("Failed parsing clipboard: ", e) } }; @@ -4458,7 +4459,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Get environments error: ", error.toString()); }); }; @@ -5478,7 +5479,7 @@ const AngularWorkflow = (defaultprops) => { //} const parsedSelection = cy.$(":selected"); if (selectedNode.data().decorator === true && selectedNode.data("type") !== "COMMENT") { - alert.info("This node can't be deleted."); + toast("This node can't be deleted."); } else { console.log("Deleted.") selectedNode.remove(); @@ -5545,7 +5546,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error("ERROR: " + error.toString()); + //toast("ERROR: " + error.toString()); console.log("ERROR getting usecases: " + error.toString()); }) } @@ -5574,7 +5575,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error("ERROR: " + error.toString()); + //toast("ERROR: " + error.toString()); console.log("ERROR getting usecases: " + error.toString()); }) } @@ -5749,7 +5750,7 @@ const AngularWorkflow = (defaultprops) => { console.log("END: ", cy) var cydata = cy.$(":selected").jsons(); if (cydata !== undefined && cydata !== null && cydata.length > 0) { - alert.success(`Selected ${cydata.length} element(s). CTRL+C to copy them.`); + toast(`Selected ${cydata.length} element(s). CTRL+C to copy them.`); } }); @@ -5804,10 +5805,10 @@ const AngularWorkflow = (defaultprops) => { // No matter what, it's being stopped. if (!responseJson.success) { if (responseJson.reason !== undefined) { - alert.error("Failed to stop schedule: " + responseJson.reason); + toast("Failed to stop schedule: " + responseJson.reason); } } else { - alert.success("Successfully stopped schedule"); + toast("Successfully stopped schedule"); } workflow.triggers[triggerindex].status = "stopped"; @@ -5817,14 +5818,14 @@ const AngularWorkflow = (defaultprops) => { saveWorkflow(workflow); }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Stop schedule error: ", error.toString()); }); }; const submitSchedule = (trigger, triggerindex) => { if (trigger.name.length <= 0) { - alert.error("Error: name can't be empty"); + toast("Error: name can't be empty"); return; } @@ -5841,7 +5842,7 @@ const AngularWorkflow = (defaultprops) => { } } - alert.info("Creating schedule") + toast("Creating schedule") const data = { name: trigger.name, frequency: workflow.triggers[triggerindex].parameters[0].value, @@ -5872,9 +5873,9 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { - alert.error("Failed to set schedule: " + responseJson.reason); + toast("Failed to set schedule: " + responseJson.reason); } else { - alert.success("Successfully created schedule"); + toast("Successfully created schedule"); workflow.triggers[triggerindex].status = "running"; trigger.status = "running"; setSelectedTrigger(trigger); @@ -5884,7 +5885,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Get schedule error: ", error.toString()); }); }; @@ -6397,7 +6398,7 @@ const AngularWorkflow = (defaultprops) => { currentnode[0].renderedPosition("y", e.pageY - cycontainer.offsetTop); } else { if (workflow.start === "" || workflow.start === undefined) { - alert.error("Define a starting action first."); + toast("Define a starting action first."); return; } @@ -6602,7 +6603,7 @@ const AngularWorkflow = (defaultprops) => { app.actions === null || app.actions.length === 0 ) { - alert.error( + toast( "App " + app.name + " currently has no actions to perform. Please go to https://shuffler.io/apps to edit it." @@ -7131,13 +7132,13 @@ const AngularWorkflow = (defaultprops) => { return (
{ //if (!isCloud) { - // alert.info("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.") + // toast("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.") // setTimeout(() => { // event.preventDefault() // window.open(parsedUrl, '_blank') // }, 2000) //} else { - alert.info(`Activating ${name}`) + toast(`Activating ${name}`) //} console.log("CLICK: ", hit) @@ -7346,7 +7347,7 @@ const AngularWorkflow = (defaultprops) => { ); if (newaction === undefined || newaction === null) { - alert.error("Failed to find the action"); + toast("Failed to find the action"); return; } @@ -7696,7 +7697,7 @@ const AngularWorkflow = (defaultprops) => { // Max 1 folder for office for some reason. MailFolders('MAILBOX_ID') in resource // Can't parse URL with multiple folders. if (selectedTrigger.name === "Office365" & value !== undefined && value !== null && value.length > 1) { - alert.info("Max 1 folder at a time allowed for Office365") + toast("Max 1 folder at a time allowed for Office365") console.log("VALUE: ", value) value = [value[0]] } @@ -8900,7 +8901,7 @@ const AngularWorkflow = (defaultprops) => { (branch) => branch.source_id === selectedTrigger.id ); if (branch === undefined || branch === null) { - alert.error( + toast( "No startnode connected to node. Connect it to an action." ); return; @@ -8996,7 +8997,7 @@ const AngularWorkflow = (defaultprops) => { (branch) => branch.source_id === selectedTrigger.id ); if (branch === undefined || branch === null) { - alert.error( + toast( "No startnode connected to node. Connect it to an action." ); return; @@ -10982,7 +10983,7 @@ const AngularWorkflow = (defaultprops) => { console.log("NAVIGATOR: ", navigator); const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -10995,7 +10996,7 @@ const AngularWorkflow = (defaultprops) => { /* Copy the text inside the text field */ document.execCommand("copy"); - alert.success("Copied Webhook URL"); + toast("Copied Webhook URL"); } else { console.log("Couldn't find webhook URI field: ", copyText); } @@ -11207,7 +11208,7 @@ const AngularWorkflow = (defaultprops) => { return; } - alert.info("Stopping mail trigger"); + toast("Stopping mail trigger"); const requesttype = triggerAuthentication.type; fetch( `${globalUrl}/api/v1/workflows/${props.match.params.key}/${requesttype}/${trigger.id}`, @@ -11229,7 +11230,7 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (responseJson.success) { - alert.success("Successfully stopped trigger"); + toast("Successfully stopped trigger"); // Set the status workflow.triggers[triggerindex].status = "stopped"; trigger.status = "stopped"; @@ -11237,11 +11238,11 @@ const AngularWorkflow = (defaultprops) => { setSelectedTrigger(trigger); saveWorkflow(workflow); } else { - alert.error("Failed stopping trigger: " + responseJson.reason); + toast("Failed stopping trigger: " + responseJson.reason); } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Stop mailsub error: ", error.toString()); }); }; @@ -11263,7 +11264,7 @@ const AngularWorkflow = (defaultprops) => { const item = splitItem[splitkey]; const curfolder = triggerFolders.find((a) => a.displayName === item); if (curfolder === undefined) { - alert.error("Something went wrong with folder selection: " + item); + toast("Something went wrong with folder selection: " + item); return; } @@ -11277,7 +11278,7 @@ const AngularWorkflow = (defaultprops) => { }; const requesttype = triggerAuthentication.type; - alert.info( + toast( "Creating " + requesttype + " subscription with name " + trigger.name ); @@ -11306,9 +11307,9 @@ const AngularWorkflow = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { - alert.error("Failed to start trigger: " + responseJson.reason); + toast("Failed to start trigger: " + responseJson.reason); } else { - alert.success( + toast( "Successfully started folder subscription trigger. Test it by sending yoursend an email" ); @@ -11320,7 +11321,7 @@ const AngularWorkflow = (defaultprops) => { } }) .catch((error) => { - //alert.error(error.toString()); + //toast(error.toString()); console.log("Start mailsub error: ", error.toString()); }); }; @@ -11328,12 +11329,12 @@ const AngularWorkflow = (defaultprops) => { const newWebhook = (trigger) => { const hookname = trigger.label; if (hookname.length === 0) { - alert.error("Missing name"); + toast("Missing name"); return; } if (trigger.id.length !== 36) { - alert.error("Missing id"); + toast("Missing id"); return; } @@ -11343,10 +11344,10 @@ const AngularWorkflow = (defaultprops) => { (branch) => branch.source_id === trigger.id ); if (branch === undefined && (workflow.start === undefined || workflow.start === null || workflow.start.length === 0)) { - alert.error("No webhook node defined"); + toast("No webhook node defined"); } - alert.info("Starting webhook"); + toast("Starting webhook"); if (branch !== undefined) { startNode = branch.destination_id; } @@ -11398,14 +11399,14 @@ const AngularWorkflow = (defaultprops) => { .then((responseJson) => { if (responseJson.success) { // Set the status - alert.success("Successfully started webhook"); + toast("Successfully started webhook"); trigger.status = "running"; setSelectedTrigger(trigger); workflow.triggers[selectedTriggerIndex].status = "running"; setWorkflow(workflow); saveWorkflow(workflow); } else { - alert.error("Failed starting webhook: " + responseJson.reason); + toast("Failed starting webhook: " + responseJson.reason); } }) .catch((error) => { @@ -11444,7 +11445,7 @@ const AngularWorkflow = (defaultprops) => { saveWorkflow(workflow); } else { if (responseJson.reason !== undefined) { - alert.error("Failed stopping webhook: " + responseJson.reason); + toast("Failed stopping webhook: " + responseJson.reason); } } @@ -11453,8 +11454,8 @@ const AngularWorkflow = (defaultprops) => { setSelectedTrigger(trigger); }) .catch((error) => { - //alert.error(error.toString()); - alert.error("Delete webhook error. Contact support or check logs if this persists.") + //toast(error.toString()); + toast("Delete webhook error. Contact support or check logs if this persists.") }); }; @@ -13406,7 +13407,7 @@ const AngularWorkflow = (defaultprops) => { const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -13423,7 +13424,7 @@ const AngularWorkflow = (defaultprops) => { document.execCommand("copy"); console.log("COPYING!"); - alert.info("Copied value to clipboard, NOT json path.") + toast("Copied value to clipboard, NOT json path.") } else { console.log("Failed to copy from " + elementName + ": ", copyText); } @@ -13494,7 +13495,7 @@ const AngularWorkflow = (defaultprops) => { console.log("NAVIGATOR: ", navigator); const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -13505,7 +13506,7 @@ const AngularWorkflow = (defaultprops) => { /* Copy the text inside the text field */ document.execCommand("copy"); console.log("COPYING!"); - alert.info("Copied JSON path to clipboard.") + toast("Copied JSON path to clipboard.") } else { console.log("Couldn't find element ", elementName); } @@ -14875,7 +14876,7 @@ const AngularWorkflow = (defaultprops) => { console.log("NAVIGATOR: ", navigator); const clipboard = navigator.clipboard; if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)"); + toast("Can only copy over HTTPS (port 3443)"); return; } @@ -15480,7 +15481,7 @@ const AngularWorkflow = (defaultprops) => { selectedApp.authentication.parameters[paramkey].name ] = "false"; } else { - alert.info( + toast( "Field " + selectedApp.authentication.parameters[paramkey].name + " can't be empty" diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index cb77cf0e..9b7646bd 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -46,7 +46,8 @@ import { v4 as uuidv4 } from "uuid"; import { Link, useParams } from "react-router-dom"; import YAML from "yaml"; import { MuiChipsInput } from "mui-chips-input"; -import { useAlert } from "react-alert"; +//import { useAlert +import { ToastContainer, toast } from "react-toastify" import words from "shellwords"; import AvatarEditor from "react-avatar-editor"; @@ -350,7 +351,7 @@ const getJsonObject = (properties) => { const AppCreator = (defaultprops) => { const { globalUrl, isLoaded } = defaultprops; const classes = useStyles(); - const alert = useAlert(); + //const alert = useAlert(); const params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) @@ -451,7 +452,7 @@ const AppCreator = (defaultprops) => { }) .then((responseJson) => { if (responseJson.success === false) { - alert.error("Failed to get the app"); + toast("Failed to get the app"); setIsAppLoaded(true); window.location.pathname = "/search"; } else { @@ -459,7 +460,7 @@ const AppCreator = (defaultprops) => { } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -494,14 +495,14 @@ const AppCreator = (defaultprops) => { .then((responseJson) => { setIsAppLoaded(true); if (!responseJson.success) { - alert.error("Failed to get app config. Do you have access?"); + toast("Failed to get app config. Do you have access?"); } else { parseIncomingOpenapiData(responseJson); } }) .catch((error) => { console.log("Error: ", error.toString()); - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -568,7 +569,7 @@ const AppCreator = (defaultprops) => { } if (data.openapi === null) { - alert.info("Failed to load OpenAPI for app. Please contact support if this persists.") + toast("Failed to load OpenAPI for app. Please contact support if this persists.") setIsAppLoaded(true); return } @@ -600,7 +601,7 @@ const AppCreator = (defaultprops) => { } if (!jsonvalid) { - alert.info("OpenAPI data is invalid."); + toast("OpenAPI data is invalid."); return; } @@ -726,7 +727,7 @@ const AppCreator = (defaultprops) => { for (let [method, methodvalue] of Object.entries(pathvalue)) { if (methodvalue === null) { - alert.info("Skipped method (null)" + method); + toast("Skipped method (null)" + method); continue; } @@ -734,7 +735,7 @@ const AppCreator = (defaultprops) => { // Typical YAML issue if (method !== "parameters") { console.log("Invalid method: ", method, "data: ", methodvalue); - //alert.info("Skipped method (not allowed): " + method); + //toast("Skipped method (not allowed): " + method); } continue; } @@ -1596,7 +1597,7 @@ const AppCreator = (defaultprops) => { setParameterLocation(value.in); if (!apikeySelection.includes(value.in)) { console.log("APIKEY SELECT: ", apikeySelection); - alert.error("Might be error in setting up API key authentication"); + toast("Might be error in setting up API key authentication"); } console.log("PARAM NAME: ", value.name); @@ -1637,7 +1638,7 @@ const AppCreator = (defaultprops) => { optionset = true } else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) { - //alert.info("Can't handle Oauth2 auth yet.") + //toast("Can't handle Oauth2 auth yet.") setAuthenticationOption("Oauth2"); setAuthenticationRequired(true); optionset = true @@ -1686,7 +1687,7 @@ const AppCreator = (defaultprops) => { const scopekeysplit = scopekey.split("/"); if (scopekeysplit.length < 5) { console.log("Skipping scope: ", scopekey); - alert.info("Skipping scope: " + scopekey); + toast("Skipping scope: " + scopekey); continue; } @@ -1707,7 +1708,7 @@ const AppCreator = (defaultprops) => { ); } } else { - alert.error("Couldn't handle AUTH type: ", key); + toast("Couldn't handle AUTH type: ", key); //newauth.push({ // "name": key, // "type": value.in, @@ -1716,7 +1717,7 @@ const AppCreator = (defaultprops) => { } } } catch (e) { - alert.error("Failed to handle auth") + toast("Failed to handle auth") console.log("Error: ", e) } @@ -1790,7 +1791,7 @@ const AppCreator = (defaultprops) => { //const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" if (newActions.length > 1000 && isCloud) { - alert.error("Cut down actions from " + newActions.length + " to 999 because of limit"); + toast("Cut down actions from " + newActions.length + " to 999 because of limit"); newActions = newActions.slice(0, 999); } @@ -1812,7 +1813,7 @@ const AppCreator = (defaultprops) => { // Saving the app that's been configured. // Save SAVE app const submitApp = () => { - alert.info("Uploading and building app " + name); + toast("Uploading and building app " + name); setAppBuilding(true); setErrorCode(""); @@ -1881,7 +1882,7 @@ const AppCreator = (defaultprops) => { for (let actionkey in actions) { var item = JSON.parse(JSON.stringify(actions[actionkey])) if (item.errors.length > 0) { - alert.error("Saving with error in action " + item.name); + toast("Saving with error in action " + item.name); } if (item.name === undefined && item.description !== undefined) { @@ -2107,7 +2108,7 @@ const AppCreator = (defaultprops) => { // Bad code as it doesn't allow for "anything". if (skipped) { - alert.info( + toast( "Bad configuration of " + item.name + ". Skipping because queries are invalid." @@ -2358,7 +2359,7 @@ const AppCreator = (defaultprops) => { if (authenticationOption === "API key") { if (parameterName.length === 0) { - alert.error("A field name for the APIkey must be defined"); + toast("A field name for the APIkey must be defined"); setAppBuilding(false); return; } @@ -2424,7 +2425,7 @@ const AppCreator = (defaultprops) => { const curauth = extraAuth[authkey]; if (curauth.name.toLowerCase() == "url") { - alert.error("Can't add extra auth with Name URL"); + toast("Can't add extra auth with Name URL"); setAppBuilding(false); return; } @@ -2459,10 +2460,10 @@ const AppCreator = (defaultprops) => { if (!responseJson.success) { if (responseJson.reason !== undefined) { setErrorCode(responseJson.reason); - alert.error("Failed to verify: " + responseJson.reason); + toast("Failed to verify: " + responseJson.reason); } } else { - alert.success("Successfully uploaded openapi"); + toast("Successfully uploaded openapi"); if (window.location.pathname.includes("/new")) { if (responseJson.id !== undefined && responseJson.id !== null) { window.location = `/apps/edit/${responseJson.id}`; @@ -2473,7 +2474,7 @@ const AppCreator = (defaultprops) => { .catch((error) => { setAppBuilding(false); setErrorCode(error.toString()); - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -2805,7 +2806,7 @@ const AppCreator = (defaultprops) => { !tmpstring.startsWith("http") && !tmpstring.startsWith("ftp") ) { - alert.error("Auth URL must start with http(s)://"); + toast("Auth URL must start with http(s)://"); } if (tmpstring.includes("?")) { @@ -2854,7 +2855,7 @@ const AppCreator = (defaultprops) => { !tmpstring.startsWith("http") && !tmpstring.startsWith("ftp") ) { - alert.error("Token URL must start with http(s)://"); + toast("Token URL must start with http(s)://"); } if (tmpstring.includes("?")) { @@ -2900,7 +2901,7 @@ const AppCreator = (defaultprops) => { !tmpstring.startsWith("http") && !tmpstring.startsWith("ftp") ) { - alert.error("Refresh URL must start with http(s)://"); + toast("Refresh URL must start with http(s)://"); } if (tmpstring.includes("?")) { @@ -3791,7 +3792,7 @@ const AppCreator = (defaultprops) => { value = keysplit[1].trim() } else { - alert.error("Removed key: ", key) + toast("Removed key: ", key) continue } } @@ -4626,11 +4627,11 @@ const AppCreator = (defaultprops) => { setSelectedAction(selectedAction); } - //alert.error("Failed getting authentications") + //toast("Failed getting authentications") } }) .catch((error) => { - alert.error("Auth loading error: " + error.toString()); + toast("Auth loading error: " + error.toString()); }); }; @@ -4686,17 +4687,17 @@ const AppCreator = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { - alert.error("Failed to set app auth: " + responseJson.reason); + toast("Failed to set app auth: " + responseJson.reason); } else { getAppAuthentication(true); setAuthenticationModalOpen(false); // Needs a refresh with the new authentication.. - //alert.success("Successfully saved new app auth") + //toast("Successfully saved new app auth") } }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -4747,7 +4748,7 @@ const AppCreator = (defaultprops) => { console.log("NEW AUTH: ", authenticationOption); if (authenticationOption.label.length === 0) { authenticationOption.label = `Auth for ${selectedApp.name}`; - //alert.info("Label can't be empty") + //toast("Label can't be empty") //return } @@ -4757,7 +4758,7 @@ const AppCreator = (defaultprops) => { selectedApp.authentication.parameters[key].name ].length === 0 ) { - alert.info( + toast( "Field " + selectedApp.authentication.parameters[key].name + " can't be empty" @@ -5165,7 +5166,7 @@ const AppCreator = (defaultprops) => { setFileBase64(canvasUrl); } } catch (e) { - alert.error("Failed to parse canvasurl!"); + toast("Failed to parse canvasurl!"); } }; @@ -5253,7 +5254,7 @@ const AppCreator = (defaultprops) => { setOpenImageModal(false); setDisableImageUpload(true); } catch (e) { - alert.error("Failed to set image. Replace it if this persists."); + toast("Failed to set image. Replace it if this persists."); } } }; @@ -5468,7 +5469,7 @@ const AppCreator = (defaultprops) => { const invalid = ["#", ":", "."]; for (var key in invalid) { if (e.target.value.includes(invalid[key])) { - alert.error("Can't use " + invalid[key] + " in name"); + toast("Can't use " + invalid[key] + " in name"); setName(e.target.value.replaceAll(".", "").replaceAll("#", "").replaceAll(":", "").replaceAll(",", "")) return; @@ -5476,7 +5477,7 @@ const AppCreator = (defaultprops) => { } if (e.target.value.length > 29) { - alert.error("Choose a shorter name (max 29)."); + toast("Choose a shorter name (max 29)."); setName(e.target.value.slice(0,28)) return; } @@ -5588,7 +5589,7 @@ const AppCreator = (defaultprops) => { !tmpstring.startsWith("http") && !tmpstring.startsWith("ftp") ) { - alert.error("URL must start with http(s)://"); + toast("URL must start with http(s)://"); } if (tmpstring.includes("?")) { diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index dccc81bc..c9ee901c 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -59,7 +59,8 @@ import algoliasearch from 'algoliasearch/lite'; import YAML from "yaml"; import { useNavigate, Link, useParams } from "react-router-dom"; -import { useAlert } from "react-alert"; +//import { useAlert +import { ToastContainer, toast } from "react-toastify" import Dropzone from "../components/Dropzone.jsx"; const surfaceColor = "#27292D"; @@ -272,7 +273,7 @@ const Apps = (props) => { //const [workflows, setWorkflows] = React.useState([]); const baseRepository = "https://github.com/frikky/shuffle-apps"; - const alert = useAlert(); + //const alert = useAlert(); let navigate = useNavigate(); const [selectedApp, setSelectedApp] = React.useState({}); @@ -454,7 +455,7 @@ const Apps = (props) => { //}, 5000) }) .catch((error) => { - alert.error(error.toString()); + toast(error.toString()); setIsLoading(false); }); }; @@ -462,7 +463,7 @@ const Apps = (props) => { const downloadApp = (inputdata) => { const id = inputdata.id; - alert.info("Downloading.."); + toast("Downloading.."); fetch(globalUrl + "/api/v1/apps/" + id + "/config", { method: "GET", headers: { @@ -480,7 +481,7 @@ const Apps = (props) => { }) .then((responseJson) => { if (!responseJson.success) { - alert.error("Failed to download file"); + toast("Failed to download file"); } else { console.log(responseJson); const basedata = atob(responseJson.openapi); @@ -538,7 +539,7 @@ const Apps = (props) => { }) .catch((error) => { console.log(error); - alert.error(error.toString()); + toast(error.toString()); }); }; @@ -1152,7 +1153,7 @@ const Apps = (props) => { { const [menuPosition, setMenuPosition] = useState(null); useEffect(() => { - if ( - selectedActionParameters !== null && - selectedActionParameters.length === 0 + if (selectedActionParameters !== undefined && selectedActionParameters !== null && selectedActionParameters.length === 0 ) { - if ( - selectedAction.parameters !== null && - selectedAction.parameters.length > 0 - ) { + if (selectedAction.parameters !== undefined && selectedAction.parameters !== null && selectedAction.parameters.length > 0) { setSelectedActionParameters(selectedAction.parameters); } } - if ( - (selectedVariableParameter === null || - selectedVariableParameter === undefined) && - workflow.workflow_variables !== null && - workflow.workflow_variables.length > 0 - ) { + if ((selectedVariableParameter === null || selectedVariableParameter === undefined) && workflow.workflow_variables !== null && workflow.workflow_variables.length > 0) { + // FIXME - this is the bad thing setSelectedVariableParameter(workflow.workflow_variables[0].name); } if (actionlist.length === 0) { // FIXME: Have previous execution values in here - if (workflowExecutions.length > 0) { - for (let [key,keyval] in Object.entries(workflowExecutions)) { - if ( - workflowExecutions[key].execution_argument === undefined || - workflowExecutions[key].execution_argument === null || - workflowExecutions[key].execution_argument.length === 0 - ) { - continue; - } - - const valid = validateJson(workflowExecutions[key].execution_argument) - if (valid.valid) { - actionlist.push({ - type: "Execution Argument", - name: "Execution Argument", - value: "$exec", - highlight: "exec", - autocomplete: "exec", - example: valid.result, - }) - break - } + if (workflowExecutions.length > 0) { + for (let [key,keyval] in Object.entries(workflowExecutions)) { + if ( + workflowExecutions[key].execution_argument === undefined || + workflowExecutions[key].execution_argument === null || + workflowExecutions[key].execution_argument.length === 0 + ) { + continue; } + const valid = validateJson(workflowExecutions[key].execution_argument) + if (valid.valid) { + actionlist.push({ + type: "Execution Argument", + name: "Execution Argument", + value: "$exec", + highlight: "exec", + autocomplete: "exec", + example: valid.result, + }) + break + } } - if (actionlist.length === 0) { - actionlist.push({ - type: "Execution Argument", - name: "Execution Argument", - value: "$exec", - highlight: "exec", - autocomplete: "exec", - example: "", - }) - } + } + + if (actionlist.length === 0) { + actionlist.push({ + type: "Execution Argument", + name: "Execution Argument", + value: "$exec", + highlight: "exec", + autocomplete: "exec", + example: "", + }) + } actionlist.push({ type: "Shuffle DB", @@ -1313,26 +1304,26 @@ const ParsedAction = (props) => { data.value = data.example; } - // In case of data.example - if (data.value === undefined || data.value === null) { + // In case of data.example + if (data.value === undefined || data.value === null) { + data.value = "" + } + + if (data.value.length === 0) { + if (data.name.toLowerCase() === "headers") { + console.log("Should show headers field instead with + and -!") + + // Check if file ID exists + // + const fileFound = selectedActionParameters.find(param => param.name === "file_id") + if (fileFound === undefined || fileFound === null) { + data.value = data.example + } else { + // Purposely unset it if set by default when using files data.value = "" } - - if (data.value.length === 0) { - if (data.name.toLowerCase() === "headers") { - console.log("Should show headers field instead with + and -!") - - // Check if file ID exists - // - const fileFound = selectedActionParameters.find(param => param.name === "file_id") - if (fileFound === undefined || fileFound === null) { - data.value = data.example - } else { - // Purposely unset it if set by default when using files - data.value = "" - } - } - } + } + } /* if (data.name !== "queries" && data.name !== "key" && data.name !== "value" ) { @@ -1391,7 +1382,7 @@ const ParsedAction = (props) => { var hideBodyButton = ""; const hideBodyButtonValue = (
{ > { for (let paramkey in Object.entries(selectedActionParameters)) { var currentItem = selectedActionParameters[paramkey]; - if (currentItem.name === "ssl_verify") { + if (currentItem.name === "ssl_verify") { - } + } - if (currentItem.name === "body") { - // FIXME: Workaround for toggling, as actions don't have IDs. - // May screw up something in the future. - currentItem.id = tag - } + if (currentItem.name === "body") { + // FIXME: Workaround for toggling, as actions don't have IDs. + // May screw up something in the future. + currentItem.id = tag + } if (currentItem.description === openApiFieldDesc) { currentItem.field_active = !hideBody; @@ -1458,8 +1449,8 @@ const ParsedAction = (props) => { if (found === null) { setActivateHidingBodyButton(true); } else { - //console.log("In found: ", found, hideBody) - } + //console.log("In found: ", found, hideBody) + } } else { //console.log("SHOW BUTTON"); @@ -1485,6 +1476,17 @@ const ParsedAction = (props) => { } changed = true; + var isRequired = false + // Check if original field name is in the selectedAction.required_body_fields + if (selectedAction.required_body_fields !== undefined && selectedAction.required_body_fields !== null) { + for (let innerkey in selectedAction.required_body_fields) { + if (selectedAction.required_body_fields[innerkey] === tmpitem) { + isRequired = true + break + } + } + } + selectedActionParameters.push({ action_field: "", configuration: false, @@ -1494,7 +1496,7 @@ const ParsedAction = (props) => { multiline: true, name: tmpitem, options: null, - required: false, + required: isRequired, schema: { type: "string" }, skip_multicheck: false, tags: null, @@ -2443,8 +2445,8 @@ const ParsedAction = (props) => { //console.log(data.configuration) - const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}` - const hasAutocomplete = data.autocompleted === true + const buttonTitle = `Authenticate ${selectedApp.name.replaceAll("_", " ")}` + const hasAutocomplete = data.autocompleted === true return (
{hideBodyButton} @@ -2669,8 +2671,6 @@ const ParsedAction = (props) => { const { data, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops; const [hover, setHover] = React.useState(false); - console.log("Extra desc: ", extraDescription) - return ( { > - - - + + + + + + { + // aiSubmit(aiMsg, undefined, undefined, newSelectedAction) + aiSubmit("Fill based on previous values", undefined, undefined, selectedAction) + }} + > + + -
@@ -2992,14 +3012,21 @@ const ParsedAction = (props) => { defaultValue={selectedAction.label} onChange={selectedNameChange} onBlur={(e) => { - const name = e.target.value; + // Copy the name value + const name = e.target.value const parsedBaseLabel = "$"+baselabel.toLowerCase().replaceAll(" ", "_") const newname = "$"+name.toLowerCase().replaceAll(" ", "_") + console.log("NAME: ", name) + + // Check if it's the same as the current name in use + //if (name === selectedAction.label) { + // console.log("Returning from name thing") + // return + //} + // Change in actions, triggers & conditions // Highlight the changes somehow with a glow? - // - // Should make it a function lol if (workflow.branches !== undefined && workflow.branches !== null) { for (let [key,keyval] in Object.entries(workflow.branches)) { if (workflow.branches[key].conditions !== undefined && workflow.branches[key].conditions !== null) { @@ -3117,7 +3144,13 @@ const ParsedAction = (props) => { continue } - for (let [subkey, subkeyval] in Object.entries(workflow.actions[key].parameters)) { + const params = workflow.actions[key].parameters + console.log(params) + if (params === null || params === undefined) { + continue + } + + for (let [subkey, subkeyval] in Object.entries(params)) { const param = workflow.actions[key].parameters[subkey]; if (!param.value.includes("$")) { continue @@ -3179,7 +3212,7 @@ const ParsedAction = (props) => { console.log("DID NAME REPLACE ACTUALLY WORK? - may be missing it in certain triggers"); setWorkflow(workflow); - setUpdate(Math.random()); + setUpdate(Math.random()); baselabel = name }} /> @@ -3240,7 +3273,7 @@ const ParsedAction = (props) => { }} > Authenticate{" "} - {selectedApp.name} + {selectedApp.name.replaceAll("_", " ")} @@ -3573,8 +3606,6 @@ const ParsedAction = (props) => { data.label = "No name" } - console.log("Name: ", newActionname) - newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " "); var method = "" diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 1b3c9d68..3bafef61 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -43,6 +43,7 @@ const SearchField = props => { const node = useRef() const [searchOpen, setSearchOpen] = useState(false) const [oldPath, setOldPath] = useState("") + const [value, setValue] = useState(""); if (serverside === true) { return null @@ -67,7 +68,13 @@ const SearchField = props => { //}, searchOpen) const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => { - + const keyPressHandler = (e) => { + // e.preventDefault(); + if (e.which === 13) { + // alert("You pressed enter!"); + navigate("/search?q=" + currentRefinement, { state: value, replace: true }); + } + }; /* endAdornment: ( { @@ -108,6 +115,7 @@ const SearchField = props => { color="primary" placeholder="Find Public Apps, Workflows, Documentation..." value={currentRefinement} + onKeyDown={keyPressHandler} id="shuffle_search_field" onClick={(event) => { if (!searchOpen) { diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index a9599819..fda866b2 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -205,6 +205,9 @@ const CodeEditor = (props) => { setAvailableVariables(allVariables) setMainVariables(tmpVariables) + + console.log("Checking local codedata: ", localcodedata) + expectedOutput(localcodedata) }, []) const aiSubmit = (value, inputAction) => { diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index e721dc3f..f99bbc52 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import {Link} from 'react-router-dom'; import theme from '../theme.jsx'; +import { removeQuery } from '../components/ScrollToTop.jsx'; import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material'; @@ -171,6 +172,7 @@ const AppGrid = props => { // value={currentRefinement} const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { + var defaultSearch = "" useEffect(() => { if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) { const urlSearchParams = new URLSearchParams(window.location.search) @@ -179,6 +181,7 @@ const AppGrid = props => { if (foundQuery !== null && foundQuery !== undefined) { console.log("Got query: ", foundQuery) refine(foundQuery) + defaultSearch = foundQuery } } }, []) @@ -187,6 +190,7 @@ const AppGrid = props => { console.log("In refinement: ", inputsearch) //setLocalMessage(inputsearch) refine(inputsearch) + defaultSearch = inputsearch } else if (onlyResults === true) { // Don't return anything unless refinement works return null @@ -196,6 +200,7 @@ const AppGrid = props => {
{onlyResults !== true ? { placeholder="Find Workflows..." id="shuffle_search_field" onChange={(event) => { + removeQuery("q") refine(event.currentTarget.value) }} limit={5} diff --git a/frontend/src/defaultCytoscapeStyle.jsx b/frontend/src/defaultCytoscapeStyle.jsx index f9e09f22..01c389f8 100644 --- a/frontend/src/defaultCytoscapeStyle.jsx +++ b/frontend/src/defaultCytoscapeStyle.jsx @@ -63,7 +63,7 @@ const data = [ "z-index": 4999, "border-radius": "5px", "background-opacity": "0.5", - "text-wrap": "wrap", + "text-wrap": "wrap", }, }, { @@ -125,8 +125,8 @@ const data = [ "border-radius": "5px", "border-color": "orange", "background-color": "#213243", - "background-width": "100%", - "background-height": "100%", + "background-width": "100px", + "background-height": "100px", }, }, { @@ -163,15 +163,16 @@ const data = [ { selector: "node[?isSuggestion]", css: { - shape: "ellipse", - width: "50px", - height: "50px", + shape: "roundrectangle", + width: "30px", + height: "30px", "z-index": "5002", - "font-size": "0px", + filter: "grayscale(100%)", border: "1px solid rgba(255,255,255,0.9)", "background-image": "data(large_image)", - "background-color": "data(iconBackground)", - label: "data(label)", + "background-fit": "cover", + "font-size": "20px", + label: "data(label_replaced)", }, }, { @@ -199,7 +200,7 @@ const data = [ "text-margin-x": "0px", "background-color": "data(imageColor)", "background-image": "data(image)", - label: "data(label)", + label: "data(label)", }, }, { @@ -337,9 +338,9 @@ const data = [ width: "1px", "line-style": "dashed", "line-fill": "linear-gradient", - "target-arrow-color": "#f34079", + "target-arrow-color": "#555555", "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["#f86a3e", "#f34079"], + "line-gradient-stop-colors": ["#555555", "#555555"], }, }, { diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index 0ecece8d..e24f7a0a 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -24,12 +24,12 @@ const theme = createTheme(adaptV4Theme({ backgroundColor: "#1a1a1a", borderRadius: 5, defaultBorder: "1px solid rgba(255,255,255,0.3)", - jsonTheme: "brewer", - reactJsonStyle: { - borderRadius: 5, - border: "1px solid rgba(255,255,255,0.7)", - padding: 5, - }, + jsonTheme: "brewer", + reactJsonStyle: { + borderRadius: 5, + border: "1px solid rgba(255,255,255,0.7)", + padding: 5, + }, textFieldStyle: { backgroundColor: "#383B40", borderRadius: 5, @@ -70,6 +70,11 @@ const theme = createTheme(adaptV4Theme({ }, }, overrides: { + MuiPaper: { + root: { + backgroundColor: "#1c1c1d", + }, + }, MuiMenu: { list: { backgroundColor: "#27292d", diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 95a4f042..3e54131a 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -3381,11 +3381,10 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user 0 ? schedule.frequency - ) : ( + : {schedule.seconds} seconds - ) } /> {schedule.workflow_id} } /> { const [workflowExecutions, setWorkflowExecutions] = React.useState([]); const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0); - const [workflowRecommendations, setWorkflowRecommendations] = React.useState([]); + const [workflowRecommendations, setWorkflowRecommendations] = React.useState(undefined); - const [listCache, setListCache] = React.useState([]); - const [suggestionBox, setSuggestionBox] = React.useState({ - "position": { - "top": 500, - "left": 500, - }, - "open": false, - "attachedTo": "", - }); + const [listCache, setListCache] = React.useState([]); + const [suggestionBox, setSuggestionBox] = React.useState({ + "position": { + "top": 500, + "left": 500, + }, + "open": false, + "attachedTo": "", + }); // For code editor const [codeEditorModalOpen, setCodeEditorModalOpen] = React.useState(false); @@ -556,6 +555,22 @@ const AngularWorkflow = (defaultprops) => { window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + /* + useEffect(() => { + console.log("In useeffect for workflow: ", workflow) + if (cy === undefined || cy === null) { + return + } + + if (workflow === undefined || workflow === null || workflow.actions === undefined || workflow.actions === null) { + return + } + + // Check if actions of original vs new are changed + fetchRecommendations(workflow) + }, [workflow]) + */ + const appBarSize = isCloud ? 75 : 72; const triggerEnvironments = isCloud ? ["cloud"] : ["onprem", "cloud"]; const unloadText = "Are you sure you want to leave without saving (CTRL+S)?"; @@ -1260,13 +1275,13 @@ const AngularWorkflow = (defaultprops) => { }; const sendStreamRequest = (body) => { - console.log("Stream not activated yet.") - return + //console.log("Stream not activated yet.") + //return // Session may be important here huh body.user_id = userdata.id - fetch(globalUrl + "/api/v1/workflows/" + props.match.params.key + "/stream", { + fetch(`${globalUrl}/api/v1/workflows/${props.match.params.key}/stream`, { method: "POST", headers: { "Content-Type": "application/json", @@ -1278,13 +1293,13 @@ const AngularWorkflow = (defaultprops) => { .then((response) => { setSavingState(0); if (response.status !== 200) { - console.log("Status not 200 for setting workflows :O!"); + //console.log("Status not 200 for stream :O!"); } return response.json(); }) .then((responseJson) => { - console.log("RESP: ", responseJson) + console.log("Stream resp: ", responseJson) }) .catch((error) => { console.log("Stream send error: ", error.toString()) @@ -1454,7 +1469,7 @@ const AngularWorkflow = (defaultprops) => { curworkflowComment.width = 200 } - curworkflowComment.position = cyelements[cyelementsKey].position(); + curworkflowComment.position = cyelements[cyelementsKey].position(); //console.log(curworkflowComment) newComments.push(curworkflowComment); @@ -1478,6 +1493,17 @@ const AngularWorkflow = (defaultprops) => { useworkflow.errors = []; useworkflow.previously_saved = true; + // Find the startnode in actions + /* + var foundStartNode = useworkflow.actions.find((a) => a.is_start_node === true) + console.log("Discovered startnode: ", foundStartNode) + if ((foundStartNode === undefined || foundStartNode === null) && useworkflow.actions.length > 0) { + // Set a startnode + useworkflow.actions[0].is_start_node = true + useworkflow.start = useworkflow.actions[0].id + } + */ + if (cy !== undefined) { // scale: 0.3, // bg: "#27292d", @@ -1629,10 +1655,10 @@ const AngularWorkflow = (defaultprops) => { setExecutionRequest({}) stop() - var curelements = cy.elements(); - for (let i = 0; i < curelements.length; i++) { - curelements[i].addClass("not-executing-highlight"); - } + var curelements = cy.elements(); + for (let i = 0; i < curelements.length; i++) { + curelements[i].addClass("not-executing-highlight"); + } const data = { execution_argument: executionArgument, start: startNode }; fetch( @@ -1744,6 +1770,7 @@ const AngularWorkflow = (defaultprops) => { if (cy !== undefined) { // Remove the old listener for select, run with new one cy.removeListener("select"); + cy.on("select", "node", (e) => onNodeSelect(e, newauth)); cy.on("select", "edge", (e) => onEdgeSelect(e)); } @@ -1863,12 +1890,15 @@ const AngularWorkflow = (defaultprops) => { "large_image": "", }] - setAppsLoaded(true) + setAppsLoaded(true) setApps(pretend_apps) setFilteredApps(pretend_apps) setPrioritizedApps(pretend_apps); - toast("Something went wrong while loading apps. Please refresh the window to try again.") + if (isLoggedIn) { + toast("Something went wrong while loading apps. Please refresh the window to try again.") + } + return } @@ -2012,7 +2042,516 @@ const AngularWorkflow = (defaultprops) => { //toast(error.toString()); console.log("Error loading files: ", error) }); - }; + }; + + const onChunkedResponseComplete = (result) => { + // Dont return until in 5 seconds without setTimeout + } + + const onChunkedResponseError = (err) => { + console.error(err) + } + + + const uuidToHSV = (uuid) => { + // Convert the UUID to a hexadecimal string without dashes + const uuidHex = uuid.replace(/-/g, ""); + + // Take the first 6 characters of the hexadecimal UUID as the seed + const seed = parseInt(uuidHex.slice(0, 6), 16); + + // Normalize the seed to a value between 0 and 1 + const normalizedSeed = seed / 0xFFFFFF; // 0xFFFFFF is the maximum possible value with 6 hexadecimal characters + + // Use the normalized seed to generate HSV values + const hue = normalizedSeed; // Hue value between 0 and 1 + const saturation = 0.8; // You can adjust the saturation value as desired (between 0 and 1) + const value = 0.8; // You can adjust the value/brightness as desired (between 0 and 1) + + // Convert HSV to RGB + const rgb = HSVtoRGB(hue, saturation, value); + + // Scale the RGB values to the 0-255 range + const scaledRGB = rgb.map(val => Math.round(val * 255)); + + return scaledRGB; + } + + // HSV to RGB conversion function + const HSVtoRGB = (h, s, v) => { + const h_i = Math.floor(h * 6); + const f = h * 6 - h_i; + const p = v * (1 - s); + const q = v * (1 - f * s); + const t = v * (1 - (1 - f) * s); + + switch (h_i % 6) { + case 0: return [v, t, p]; + case 1: return [q, v, p]; + case 2: return [p, v, t]; + case 3: return [p, q, v]; + case 4: return [t, p, v]; + case 5: return [v, p, q]; + default: return [0, 0, 0]; + } + } + + const rgbToHex = (rgb) => { + // Ensure that each component is in the valid range (0-255) + const r = Math.max(0, Math.min(255, rgb[0])); + const g = Math.max(0, Math.min(255, rgb[1])); + const b = Math.max(0, Math.min(255, rgb[2])); + + // Convert the RGB values to hexadecimal and pad with zeros if needed + const rHex = r.toString(16).padStart(2, "0"); + const gHex = g.toString(16).padStart(2, "0"); + const bHex = b.toString(16).padStart(2, "0"); + + // Combine the hexadecimal values to create the final color code + const hexColor = `#${rHex}${gHex}${bHex}`; + + return hexColor.toUpperCase(); // Optionally, make the result uppercase + } + + const getUserColor = (user_id) => { + //return "#ffffff" + return rgbToHex(uuidToHSV(user_id)) + } + + const hoverNode = (chunkJson) => { + // Find the node + if (cy === undefined || cy === null) { + console.log("Cy is undefined or null") + return + } + + var node = cy.getElementById(chunkJson.id) + if (node === undefined || node === null) { + console.log("Node is undefined or null") + return + } + + const color = getUserColor(chunkJson.user_id) + const parsedStyle = { + "border-width": "6px", + "border-opacity": ".7", + "font-size": "25px", + "border-color": color, + } + + const animationDuration = 150 + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + + // Wait 3 seconds and remove it + setTimeout(() => { + const parsedStyle = { + "border-width": "1px", + "font-size": "18px", + "border-color": "#81c784", + } + + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + }, 3000) + } + + const moveNode = (chunkJson) => { + // Find the node + if (cy === undefined || cy === null) { + console.log("Cy is undefined or null") + return + } + + var node = cy.getElementById(chunkJson.id) + if (node === undefined || node === null) { + console.log("Node is undefined or null") + return + } + + console.log("Moving node: ", node, chunkJson) + + // Find nodes attached to the node + node.position({ + x: chunkJson.location.x, + y: chunkJson.location.y + }) + + const connectedNodes = cy.filter('node[attachedTo = "'+chunkJson.id+'"]') + if (connectedNodes === undefined || connectedNodes === null) { + console.log("Connected nodes is undefined or null") + } else { + console.log("Connected nodes: ", connectedNodes) + connectedNodes.remove() + } + + const color = getUserColor(chunkJson.user_id) + const parsedStyle = { + "border-width": "11px", + "border-opacity": ".7", + "font-size": "25px", + "border-color": color, + } + + const animationDuration = 150 + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + + // Wait 3 seconds and remove it + setTimeout(() => { + const parsedStyle = { + "border-width": "1px", + "font-size": "18px", + "border-color": "#81c784", + } + + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + }, 3000) + } + + const hoverEdge = (chunkJson) => { + // Find the node + if (cy === undefined || cy === null) { + console.log("Cy is undefined or null") + return + } + + var node = cy.getElementById(chunkJson.id) + if (node === undefined || node === null) { + console.log("Node is undefined or null") + return + } + + const color = getUserColor(chunkJson.user_id) + const parsedStyle = { + "line-gradient-stop-positions": ["0.0", "100"], + "line-gradient-stop-colors": [color, color], + } + + const animationDuration = 150 + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + + // Wait 3 seconds and remove it + setTimeout(() => { + const parsedStyle = { + "line-gradient-stop-positions": ["0.0", "100"], + "line-gradient-stop-colors": ["grey", "grey"], + } + + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + }, 3000) + } + + const selectNode = (chunkJson) => { + if (cy === undefined || cy === null) { + console.log("Cy is undefined or null") + return + } + + var node = cy.getElementById(chunkJson.id) + if (node === undefined || node === null) { + console.log("Node is undefined or null") + return + } + + const color = getUserColor(chunkJson.user_id) + const parsedStyle = { + "border-width": "11px", + "border-opacity": ".7", + "font-size": "25px", + "border-color": color, + } + + const animationDuration = 150 + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + + setTimeout(() => { + const parsedStyle = { + "border-width": "11px", + "border-opacity": ".7", + "font-size": "25px", + "border-color": color, + } + + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + }, 3000) + } + + const unselectNode = (chunkJson) => { + if (cy === undefined || cy === null) { + console.log("Cy is undefined or null") + return + } + + var node = cy.getElementById(chunkJson.id) + if (node === undefined || node === null) { + console.log("Node is undefined or null") + return + } + + const color = getUserColor(chunkJson.user_id) + const parsedStyle = { + "border-width": "1px", + "font-size": "18px", + "border-color": "#81c784", + } + + const animationDuration = 150 + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + } + + const addNode = (chunkJson) => { + if (cy === undefined || cy === null) { + console.log("Cy is undefined or null") + return + } + + const node = cy.getElementById(chunkJson.id) + if (node !== undefined && node !== null) { + console.log("Node already exists: ", node) + return + } + + const color = getUserColor(chunkJson.user_id) + + // Create the node and add to cytoscape + const data = chunkJson.data + const nodeData = { + group: "nodes", + data: chunkJson.data, + position: { + x: data.x, + y: data.y + }, + } + + cy.add(nodeData) + + // Wait 100ms then add a style for it + setTimeout(() => { + const node = cy.getElementById(chunkJson.id) + if (node === undefined || node === null) { + console.log("Node is undefined or null during auto add from other user") + return + } + + const parsedStyle = { + "border-width": "11px", + "border-opacity": ".7", + "font-size": "25px", + "border-color": color, + } + + const animationDuration = 150 + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + }, 100) + } + + const removeNodeStream = (chunkJson) => { + if (cy === undefined || cy === null) { + console.log("Cy is undefined or null") + return + } + + const node = cy.getElementById(chunkJson.id) + if (node === undefined || node === null) { + console.log("Node is undefined or null") + return + } + + const color = getUserColor(chunkJson.user_id) + + // Animate node, then delete 1 sec later + const parsedStyle = { + "border-width": "11px", + "border-opacity": ".7", + "font-size": "25px", + "border-color": color, + } + + const animationDuration = 150 + node.animate( + { + style: parsedStyle, + }, + { + duration: animationDuration, + } + ) + + setTimeout(() => { + node.remove() + }, 1000) + } + + const processChunkedResponse = async (response) => { + console.log("In process resp!") + + var text = ''; + var reader = response.body.getReader() + var decoder = new TextDecoder(); + + const appendChunks = (result) => { + var chunk = decoder.decode(result.value || new Uint8Array, {stream: !result.done}); + console.log("Got chunk: ", chunk) + + if (chunk === undefined || chunk === null) { + console.log("Chunk is undefined or null") + } + + // Try chunk JSON loading + try { + var chunkJson = JSON.parse(chunk) + + if (chunkJson.item !== undefined && chunkJson.item !== null && chunkJson.item !== "") { + if (chunkJson.item === "node") { + if (chunkJson.type === "move") { + moveNode(chunkJson) + } else if (chunkJson.type === "hover") { + hoverNode(chunkJson) + } else if (chunkJson.type === "select") { + selectNode(chunkJson) + } else if (chunkJson.type === "unselect") { + unselectNode(chunkJson) + } else if (chunkJson.type === "add") { + addNode(chunkJson) + } else if (chunkJson.type === "remove") { + removeNodeStream(chunkJson) + } + } else if (chunkJson.item === "edge") { + if (chunkJson.type === "hover") { + // Same as node function? + //hoverEdge(chunkJson) + } + } + } + } catch (e) { + console.log("Chunk JSON error: ", e) + } + + //data.push(chunk) + //setData(data) + + //setUpdate(Math.random()); + + //console.log('got chunk of', chunk.length, 'bytes. Value: ', chunk) + text += chunk; + //console.log('text so far is', text.length, 'bytes\n'); + if (result.done) { + console.log('returning') + return text; + } else { + return readChunk() + } + } + + const readChunk = () => { + return reader.read().then(appendChunks); + } + + return readChunk(); + } + + async function fetchWithTimeout(resource, options = {}) { + const { timeout = 8000 } = options; + + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + + const response = await fetch(resource, { + ...options, + signal: controller.signal + }); + clearTimeout(id); + + return response; + } + + const startWorkflowStream = async (workflowId) => { + const timeout = 60000 + + while (true) { + await fetchWithTimeout(`${globalUrl}/api/v1/workflows/${workflowId}/stream`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + timeout: timeout, + }) + .then(processChunkedResponse) + .then(onChunkedResponseComplete) + .catch(onChunkedResponseError) + } + } const getWorkflow = (workflow_id, sourcenode) => { fetch(`${globalUrl}/api/v1/workflows/${workflow_id}`, { @@ -2025,14 +2564,14 @@ const AngularWorkflow = (defaultprops) => { }) .then((response) => { if (response.status !== 200) { - console.log("Status not 200 for workflows :O!"); + console.log("Status not 200 for workflows :O!"); - if (response.status >= 500) { - toast("Something went wrong while loading the workflow. Please reload.") - } else { - toast("You don't access to this workflow or loading failed.") - window.location.pathname = "/workflows"; - } + if (response.status >= 500) { + toast("Something went wrong while loading the workflow. Please reload.") + } else { + toast("You don't access to this workflow or loading failed.") + window.location.pathname = "/workflows"; + } } // Read text from stream @@ -2040,10 +2579,10 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - // Load as JSON - //console.log("Got workflow TXT: ", responseText) - //const responseJson = JSON.parse(responseText) - //console.log("Got workflow JSON: ", responseJson) + // Load as JSON + //console.log("Got workflow TXT: ", responseText) + //const responseJson = JSON.parse(responseText) + //console.log("Got workflow JSON: ", responseJson) // Not sure why this is necessary. if (responseJson.isValid === undefined) { @@ -2062,54 +2601,56 @@ const AngularWorkflow = (defaultprops) => { responseJson.triggers = []; } - fetchRecommendations(responseJson) + // Wait for this to finish + fetchRecommendations(responseJson) + if (responseJson.public) { - //toast("This workflow is public. Save the workflow to use it in your organization."); - - setAppAuthentication([]) + setAppAuthentication([]) console.log("RESP: ", responseJson) if (Object.getOwnPropertyNames(creatorProfile).length === 0) { //getUserProfile("frikky") getUserProfile(responseJson.id, false) } - //{appGroup.map((data, index) => { - //const [appGroup, setAppGroup] = React.useState([]); - var appsFound = [] - for (let actionkey in responseJson.actions) { - const parsedAction = responseJson.actions[actionkey] - if (parsedAction.large_image === undefined || parsedAction.large_image === null || parsedAction.large_image === "") { - continue - } - if (appsFound.findIndex(data => data.app_name === parsedAction.app_name) < 0){ - appsFound.push(parsedAction) - } - } + //{appGroup.map((data, index) => { + //const [appGroup, setAppGroup] = React.useState([]); + var appsFound = [] + for (let actionkey in responseJson.actions) { + const parsedAction = responseJson.actions[actionkey] + if (parsedAction.large_image === undefined || parsedAction.large_image === null || parsedAction.large_image === "") { + continue + } + if (appsFound.findIndex(data => data.app_name === parsedAction.app_name) < 0){ + appsFound.push(parsedAction) + } + } setAppGroup(appsFound) - appsFound = [] - for (let triggerkey in responseJson.triggers) { - const parsedAction = responseJson.triggers[triggerkey] - if (appsFound.findIndex(data => data.app_name === parsedAction.app_name) < 0){ - appsFound.push(parsedAction) - } - } + appsFound = [] + for (let triggerkey in responseJson.triggers) { + const parsedAction = responseJson.triggers[triggerkey] + if (appsFound.findIndex(data => data.app_name === parsedAction.app_name) < 0){ + appsFound.push(parsedAction) + } + } setTriggerGroup(appsFound) + setWorkflows([responseJson]) } else { getAppAuthentication(); getEnvironments(); - getWorkflowExecution(props.match.params.key, ""); - getAvailableWorkflows(-1); + getSettings(); getFiles() + getWorkflowExecution(props.match.params.key, ""); + getAvailableWorkflows(-1); } + // Appends SUBFLOWS. Does NOT run during normal grabbing of workflows. if (sourcenode.id !== undefined) { - console.log("WORKFLOW: ", responseJson); var nodefound = false; var target = sourcenode.parameters.find((item) => item.name === "startnode"); @@ -2476,13 +3017,13 @@ const AngularWorkflow = (defaultprops) => { eles: event.target, }, }) - .play() - .promise() - .then(() => { - console.log("DONE: ", workflow_id); - getWorkflow(workflow_id.value, nodedata); - cy.fit(null, 50); - }); + .play() + .promise() + .then(() => { + console.log("DONE: ", workflow_id); + getWorkflow(workflow_id.value, nodedata); + cy.fit(null, 50); + }); } }; @@ -2547,15 +3088,12 @@ const AngularWorkflow = (defaultprops) => { var paramname = ""; var idnumber = -1; if (curElement.id.startsWith("rightside_field_")) { - console.log("FOUND FIELD WITH NUMBER: ", curElement.id); - // Find exact position to put the text const idsplit = curElement.id.split("_"); console.log(idsplit); if (idsplit.length === 3 && !isNaN(idsplit[2])) { - console.log("ADDING TO PARAM ", idsplit[2]); selectedAction.parameters[idsplit[2]].value += newValue; paramname = selectedAction.parameters[idsplit[2]].name; @@ -2824,58 +3362,143 @@ const AngularWorkflow = (defaultprops) => { // onNodeClick const onNodeSelect = (event, newAppAuth) => { // Forces all states to update at the same time, - // Otherwise everything is SUPER slow + // Otherwise everything is SUPER slow + const data = event.target.data(); + if (data.isSuggestion === true) { + console.log("Suggestion! Replace with a real action.") + + const attachedToId = data.attachedTo + //event.target.data("attachedTo", "") + + const allNodes = cy.nodes().jsons(); + for (var _key in allNodes) { + const currentNode = allNodes[_key]; + // console.log("CURRENT NODE: ", currentNode) + if ((currentNode.data.isButton || currentNode.data.isSuggestion) && currentNode.data.attachedTo !== data.id) { + cy.getElementById(currentNode.data.id).remove(); + } + } + + + // Add relevant fields for the action and connect it to the parent (attachedTo) + //event.target.data("attachedTo", "") + // Decides directionality + // + const isTarget = event.target.data("isTarget") + const target = isTarget ? data.id : attachedToId + const source = isTarget ? attachedToId : data.id + const newId = uuidv4() - ReactDOM.unstable_batchedUpdates(() => { - const data = event.target.data(); + // Add a new node + const newAction = { + ...data, + } + + newAction.attachedTo = "" + newAction.isSuggestion = false + newAction.finished = true + newAction.isButton = false + newAction.private_id = "" + newAction.type = "ACTION" + if (newAction.app_name === "Shuffle Subflow") { + newAction.type = "TRIGGER" + newAction.trigger_type = "SUBFLOW" + } else { + newAction.decorator = false + newAction.suggested = true + } + + newAction.label = data.label + newAction.id = uuidv4() + // Find the app and add params? + + cy.add({ + group: "nodes", + data: newAction, + position: { + x: event.target.position().x, + y: event.target.position().y, + }, + }) + + toast("Suggestion added!") + setTimeout(() => { + const newBranch = { + source: source, + target: newAction.id, + _id: newId, + id: newId, + decorator: false, + finished: true, + } + + cy.add({ + group: "edges", + data: newBranch, + }) + + setWorkflowRecommendations(undefined) + + // Find the new node we added from newAction.id + const newActionNode = cy.getElementById(newAction.id) + if (newActionNode !== undefined && newActionNode !== null) { + newActionNode.select() + } + + aiSubmit("Fill based on previous values", undefined, undefined, newAction) + }, 1000) + return + } + + ReactDOM.unstable_batchedUpdates(() => { if (data.isButton) { - if (data.buttonType === "suggestion") { - if (cy === undefined) { - console.log("Cy not defined yet") - return - } + if (data.buttonType === "suggestion") { + if (cy === undefined) { + console.log("Cy not defined yet") + return + } - // Inject HTML at a fixed location - //const newHtml = "

Do you want to add this suggestion?

" - - // Find mouse cursor position on screen - console.log("Suggestion html to be added at location: ", event) - /* - const position = { - "top": cy.pan().y, - "left": cy.pan().x, - } - */ + // Inject HTML at a fixed location + //const newHtml = "

Do you want to add this suggestion?

" + + // Find mouse cursor position on screen + console.log("Suggestion html to be added at location: ", event) + /* + const position = { + "top": cy.pan().y, + "left": cy.pan().x, + } + */ - const position = event.target.renderedPosition(); - const container = cy.container(); - const offset = { - left: container.offsetLeft, - top: container.offsetTop - }; - - // Calculate the actual screen position for the box - const screenPosition = { - left: position.x + offset.left - 150, - top: position.y + offset.top, - }; - - // Log the position to the console - console.log('Node screen position:', screenPosition); + const position = event.target.renderedPosition(); + const container = cy.container(); + const offset = { + left: container.offsetLeft, + top: container.offsetTop + }; + + // Calculate the actual screen position for the box + const screenPosition = { + left: position.x + offset.left - 150, + top: position.y + offset.top, + }; + + // Log the position to the console + console.log('Node screen position:', screenPosition); - const newbox = { - "position": screenPosition, - "node_position": event.target.position(), - "open": true, - "attachedTo": data.attachedTo, - } + const newbox = { + "position": screenPosition, + "node_position": event.target.position(), + "open": true, + "attachedTo": data.attachedTo, + } - console.log("Rendered position: ", newbox.node_position) + console.log("Rendered position: ", newbox.node_position) - setSuggestionBox(newbox) + setSuggestionBox(newbox) - // Unselect + // Unselect event.target.unselect(); } else if (data.buttonType === "delete") { @@ -2918,7 +3541,7 @@ const AngularWorkflow = (defaultprops) => { // 2. Find branches for parent // 3. Make a new node that's moved a little bit const parentNode = cy.getElementById(data.attachedTo); - if (parentNode !== null && parentNode !== undefined) { + if (parentNode !== null && parentNode !== undefined && parentNode.data() !== undefined && parentNode.data() !== null) { var newNodeData = JSON.parse(JSON.stringify(parentNode.data())); newNodeData.id = uuidv4(); if (newNodeData.position !== undefined) { @@ -3302,10 +3925,14 @@ const AngularWorkflow = (defaultprops) => { //} if (data.app_name === "Shuffle Workflow" || data.app_name === "User Input") { - console.log("In Workflow loading for user input & subflow") - getAvailableWorkflows(trigger_index); - getSettings(); + // Check if public workflow + if (workflow.public === true) { + setWorkflows([workflow]) + } else { + getAvailableWorkflows(trigger_index); + getSettings(); + } } else if (data.app_name === "Webhook") { if (workflow.triggers[trigger_index].parameters !== undefined && workflow.triggers[trigger_index].parameters !== null && workflow.triggers[trigger_index].parameters.length > 0) { console.log("Can set params here!") @@ -3314,13 +3941,13 @@ const AngularWorkflow = (defaultprops) => { value: referenceUrl + "webhook_" + workflow.triggers[trigger_index].id, }; - if (workflow.triggers[trigger_index].parameters.length < 5) { - console.log("Adding to webhook params!") - workflow.triggers[trigger_index].parameters.push({ - name: "await_response", - value: "v1," - }) - } + if (workflow.triggers[trigger_index].parameters.length < 5) { + console.log("Adding to webhook params!") + workflow.triggers[trigger_index].parameters.push({ + name: "await_response", + value: "v1," + }) + } } } @@ -3342,16 +3969,14 @@ const AngularWorkflow = (defaultprops) => { selected: "", }); - console.log("DOne in the node update") - - setSuggestionBox({ - "position": { - "top": 500, - "left": 500, - }, - "open": false, - "attachedTo": "", - }); + setSuggestionBox({ + "position": { + "top": 500, + "left": 500, + }, + "open": false, + "attachedTo": "", + }); sendStreamRequest({ "item": "node", "type": "select", @@ -3677,7 +4302,7 @@ const AngularWorkflow = (defaultprops) => { setLastSaved(false); const edge = event.target.data(); - console.log("edge added: ", edge) + //console.log("edge added: ", edge) if (edge.source === undefined && edge.target === undefined) { return } @@ -3693,7 +4318,7 @@ const AngularWorkflow = (defaultprops) => { const destinationnode = cy.getElementById(edge.target) if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) { } else { - console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data()) + //console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data()) if (sourcenode.data("type") === "TRIGGER") { if (sourcenode.data("app_name") !== "Shuffle Workflow" && sourcenode.data("app_name") !== "User Input") { setTimeout(() => { @@ -3724,9 +4349,9 @@ const AngularWorkflow = (defaultprops) => { const edgeCurve = calculateEdgeCurve(sourcenode.position(), destinationnode.position()) const currentedge = cy.getElementById(edge.id) if (currentedge !== undefined && currentedge !== null) { - console.log("Setting edge curve: ", edgeCurve) - currentedge.style('control-point-distance', edgeCurve.distance) - currentedge.style('control-point-weight', edgeCurve.weight) + console.log("Setting edge curve: ", edgeCurve) + currentedge.style('control-point-distance', edgeCurve.distance) + currentedge.style('control-point-weight', edgeCurve.weight) } } @@ -3736,10 +4361,7 @@ const AngularWorkflow = (defaultprops) => { ); if (targetnode !== -1) { console.log("TARGETNODE: ", targetnode); - if ( - workflow.triggers[targetnode].app_name === "User Input" || - workflow.triggers[targetnode].app_name === "Shuffle Workflow" - ) { + if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") { } else { toast("Can't have triggers as target of branch"); event.target.remove(); @@ -3802,8 +4424,6 @@ const AngularWorkflow = (defaultprops) => { workflow.branches[branchkey].destination_id === edge.target && workflow.branches[branchkey].source_id === edge.source ) { - console.log(edge.source); - //toast("That branch already exists"); event.target.remove(); @@ -3867,7 +4487,7 @@ const AngularWorkflow = (defaultprops) => { newdst !== null ) { const dstdata = RunAutocompleter(newdst.data()); - console.log("AUTO DST: ", dstdata); + console.log("DST Autocompleter: ", dstdata); } var newbranch = { @@ -3910,6 +4530,11 @@ const AngularWorkflow = (defaultprops) => { return; } + // Check for recommendations when a new action is added + // if (isLoaded === true && firstrequest === false) { + // fetchRecommendations(workflow) + // } + // DONT MOVE THIS LINE RIGHT HERE v setLastSaved(false) @@ -3934,7 +4559,23 @@ const AngularWorkflow = (defaultprops) => { } } + console.log("NODE: ", nodedata) + if (nodedata.decorator !== true && nodedata.attachedTo === undefined) { + var newdata = JSON.parse(JSON.stringify(nodedata)) + newdata.large_image = "" + sendStreamRequest({ + "item": "node", + "type": "add", + "id": nodedata.id, + "data": nodedata, + "x": node.position("x"), + "y": node.position("y"), + }) + } + if (nodedata.type === "ACTION") { + // Should get recommendations to load in for all nodesma + /* var curaction = workflow.actions.find((a) => a.id === nodedata.id); if (curaction === null || curaction === undefined) { @@ -3942,10 +4583,7 @@ const AngularWorkflow = (defaultprops) => { event.target.remove(); } */ - if ( - workflow.actions.length === 1 && - workflow.actions[0].id === workflow.start - ) { + if (workflow.actions.length === 1 && workflow.actions[0].id === workflow.start) { const newEdgeUuid = uuidv4(); const newcybranch = { source: workflow.start, @@ -4014,7 +4652,14 @@ const AngularWorkflow = (defaultprops) => { workflow.actions.push(nodedata); } + // 1. Check how many actions there are. If less than three, send a toast notification with suggested workflows + //if (workflow.actions.length < 3) { + // toast("Recommendations to show??") + //} + + console.log("Added to workflow!!") setWorkflow(workflow); + fetchRecommendations(workflow) } else if (nodedata.type === "TRIGGER") { if (nodedata.is_valid === false) { toast("This trigger is not available to you"); @@ -4047,6 +4692,7 @@ const AngularWorkflow = (defaultprops) => { if (edgeToBeAdded.data.source !== edgeToBeAdded.data.target && edgeToBeAdded.data.source !== undefined && edgeToBeAdded.data.target !== undefined) { if (nodedata.name !== "User Input" && nodedata.name !== "Shuffle Workflow") { + console.log("NAME: ", nodedata.name) if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) { console.log("Edge handle: ", edgeToBeAdded) cy.add(edgeToBeAdded); @@ -4066,6 +4712,7 @@ const AngularWorkflow = (defaultprops) => { setHistory(history); setHistoryIndex(history.length); } + }; const onEdgeRemoved = (event) => { @@ -4090,7 +4737,7 @@ const AngularWorkflow = (defaultprops) => { if (curnode.data.app_name !== "Shuffle Workflow" && curnode.data.app_name !== "User Input") { // If it's started, READD the edge if (curnode.data.status === "running") { - console.log("Edge is running - readd it: ", edge.data()) + //console.log("Edge is running - readd it: ", edge.data()) // Just making sure it's not running infinitely var newdata = edge.data() @@ -4144,10 +4791,20 @@ const AngularWorkflow = (defaultprops) => { const node = event.target; const data = node.data(); + // FIXME: This is still a bit buggy + if (data.decorator !== true && data.attachedTo === undefined) { + sendStreamRequest({ + "item": "node", + "type": "remove", + "id": data.id, + }) + } + if (data.finished === false) { return } + workflow.actions = workflow.actions.filter((a) => a.id !== data.id); workflow.triggers = workflow.triggers.filter((a) => a.id !== data.id); if (workflow.start === data.id && workflow.actions.length > 0) { @@ -4478,9 +5135,37 @@ const AngularWorkflow = (defaultprops) => { const animationDuration = 150; const onNodeHoverOut = (event) => { - // console.log("Hover out: ", event.target.data()); - const nodedata = event.target.data(); + + const cytoscapeElement = document.getElementById("cytoscape_view") + if (cytoscapeElement !== undefined && cytoscapeElement !== null) { + cytoscapeElement.style.cursor = "default" + } + + if (nodedata.finished === false) { + console.log("NODE UNFINISHED HOVEROUT: ", nodedata) + + // Should just be 1, so this should be fast enough :3 + const incomingEdges = event.target.incomers("edge").jsons() + if (incomingEdges !== undefined && incomingEdges !== null) { + for (var i = 0; i < incomingEdges.length; i++) { + // Find the actual edge + const edge = cy.getElementById(incomingEdges[i].data.id) + if (edge === undefined || edge === null) { + console.log("edge is null or undefined") + continue + } + + // Set the edge to be dashed + edge.style("target-arrow-color", "#555555") + edge.style("line-style", "dashed") + edge.style("line-gradient-stop-colors", ["#555555", "#555555"]) + } + } + + return; + } + // console.log("nodedata", nodedata); // console.log("nodedata.app_name: ", nodedata.app_name); if (nodedata.app_name !== undefined) { @@ -4500,10 +5185,6 @@ const AngularWorkflow = (defaultprops) => { } } - const cytoscapeElement = document.getElementById("cytoscape_view") - if (cytoscapeElement !== undefined && cytoscapeElement !== null) { - cytoscapeElement.style.cursor = "default" - } // Skipping node editing if it's the selected one if (cy !== undefined) { @@ -4638,24 +5319,24 @@ const AngularWorkflow = (defaultprops) => { }; const addSuggestionButtons = (nodedata, event) => { - console.log("In suggestion buttons: ", nodedata, ". This is not enabled yet. Backend needs further work.") - return + //console.log("Skipping Adding suggestion buttons") + //return // Skipping add for now. Should Re-enable // Add a button for autocompletion based on input - console.log("Type: ", nodedata.type) if (nodedata.type === "ACTION") { + /* const color = "#34a853" // Fix icon - const iconInfo = { - icon: "M7.5 5.6 10 7 8.6 4.5 10 2 7.5 3.4 5 2l1.4 2.5L5 7zm12 9.8L17 14l1.4 2.5L17 19l2.5-1.4L22 19l-1.4-2.5L22 14zM22 2l-2.5 1.4L17 2l1.4 2.5L17 7l2.5-1.4L22 7l-1.4-2.5zm-7.63 5.29a.9959.9959 0 0 0-1.41 0L1.29 18.96c-.39.39-.39 1.02 0 1.41l2.34 2.34c.39.39 1.02.39 1.41 0L16.7 11.05c.39-.39.39-1.02 0-1.41l-2.33-2.35zm-1.03 5.49-2.12-2.12 2.44-2.44 2.12 2.12-2.44 2.44z", - iconColor: buttonColor, - iconBackgroundColor: buttonBackgroundColor, - }; + const iconInfo = { + icon: "M7.5 5.6 10 7 8.6 4.5 10 2 7.5 3.4 5 2l1.4 2.5L5 7zm12 9.8L17 14l1.4 2.5L17 19l2.5-1.4L22 19l-1.4-2.5L22 14zM22 2l-2.5 1.4L17 2l1.4 2.5L17 7l2.5-1.4L22 7l-1.4-2.5zm-7.63 5.29a.9959.9959 0 0 0-1.41 0L1.29 18.96c-.39.39-.39 1.02 0 1.41l2.34 2.34c.39.39 1.02.39 1.41 0L16.7 11.05c.39-.39.39-1.02 0-1.41l-2.33-2.35zm-1.03 5.49-2.12-2.12 2.44-2.44 2.12 2.12-2.44 2.44z", + iconColor: buttonColor, + iconBackgroundColor: buttonBackgroundColor, + }; - const svg_pin = ``; - const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); + const svg_pin = ``; + const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); const decoratorNode = { position: { @@ -4670,28 +5351,30 @@ const AngularWorkflow = (defaultprops) => { //label: "+", attachedTo: nodedata.id, imageColor: color, - buttonType: "suggestion", + buttonType: "suggestion", icon: svgpin_Url, iconBackground: iconInfo.iconBackgroundColor, }, }; cy.add(decoratorNode); + */ } - - //setWorkflowRecommendations(responseJson.actions) - if (workflowRecommendations.length === 0) { + + console.log("RECS: ", workflowRecommendations) + + if (workflowRecommendations === undefined || workflowRecommendations === null || workflowRecommendations.length === 0) { return } var parentNode = cy.$("#" + event.target.data("id")); if (parentNode.data("isButton") || parentNode.data("buttonId")) return; - const px = parentNode.position("x") + 300; - const py = parentNode.position("y") + -100; - const circleId = (newNodeId = uuidv4()); + const px = parentNode.position("x") + 0; + const py = parentNode.position("y") + 200; + const circleId = (newNodeId = uuidv4()); - parentNode.data("circleId", circleId); + parentNode.data("circleId", circleId); var startHeight = 0 for (let recKey in workflowRecommendations) { @@ -4700,44 +5383,108 @@ const AngularWorkflow = (defaultprops) => { continue } - if (rec.recommendations === undefined || rec.recommendations.length === 0) { + if (rec.recommendations === undefined || rec.recommendations === null || rec.recommendations.length === 0) { continue } for (let recIndex in rec.recommendations) { const parsedRec = rec.recommendations[recIndex] - startHeight += 100 + console.log("REC: ", parsedRec) const foundVersion = parsedRec.app_version !== undefined && parsedRec.app_version !== null && parsedRec.app_version !== "" ? parsedRec.app_version : "1.1.0" - const largeImage = parsedRec.large_image !== undefined && parsedRec.large_image !== null && parsedRec.large_image !== "" ? parsedRec.large_image : theme.palette.defaultImage - + const foundApp = apps.find((app) => app.app_name === parsedRec.app_name && app.app_version === foundVersion) + // Find out if foundApp is shuffle tools, and if so, add the correct image based on name - var newapp = { - app_name: parsedRec.app_name, - app_version: foundVersion, - app_id: parsedRec.app_id, - sharing: false, - private_id: false, - isStartNode: false, - label: "Suggestion "+recIndex, - large_image: largeImage, - finished: false, - is_valid: true, - isSuggestion: true, - attachedTo: event.target.data("id"), - } + const largeImage = parsedRec.large_image !== undefined && parsedRec.large_image !== null && parsedRec.large_image !== "" ? parsedRec.large_image : foundApp === undefined || foundApp === null ? theme.palette.defaultImage : foundApp.large_image - cy.add({ - group: "nodes", - data: newapp, - position: { - x: px, - y: py+startHeight, - }, - locked: true, - }); + const uuid = uuidv4() + const attachedToId = event.target.data("id") + // Check if parsedRec.app_action exists already as a node under this one + const branches = cy.edges().jsons() + var found = false + for (let branchKey in branches) { + const branch = branches[branchKey] + if (branch.data.source !== attachedToId) { + continue + } + + const targetNode = cy.getElementById(branch.data.target) + if (targetNode === undefined || targetNode === null) { + continue + } + + if (targetNode.data("name") === parsedRec.app_action) { + console.log("Found existing node (action name): ", targetNode) + found = true + } + + // FIXME: This could potentially be removed + if (targetNode.data("app_id") === parsedRec.app_id) { + console.log("Found existing node (id): ", targetNode) + found = true + } + } + + // Skip the suggestion if it already exists + if (found) { + continue + } + + // Checks for src/dst (e.g. trigger = src usually) + const isTarget = true + + var name = parsedRec.app_action + if (parsedRec.app_action === "subflow") { + name = "Shuffle Workflow" + } else if (parsedRec.app_action === "user_input") { + name = "User Input" + } + + const newaction = { + name: name, + label: parsedRec.app_action, + label_replaced: parsedRec.app_action.replace("_", " ", -1), + + id: uuid, + app_name: parsedRec.app_name, + app_version: foundVersion, + app_id: parsedRec.app_id, + sharing: false, + private_id: "", + isStartNode: false, + large_image: largeImage, + is_valid: true, + isSuggestion: true, + isTarget: isTarget, + attachedTo: attachedToId, + + finished: false, + } + + cy.add({ + group: "nodes", + data: newaction, + position: { + x: px+startHeight, + y: py, + }, + locked: true, + }); + + cy.add({ + group: "edges", + data: { + source: event.target.data("id"), + target: uuid, + decorator: true, + } + }) + + startHeight += 100 } + console.log("Got Rec: ", rec) + break } } @@ -4815,75 +5562,98 @@ const AngularWorkflow = (defaultprops) => { const onNodeHover = (event) => { const nodedata = event.target.data(); - if (nodedata.finished === false) { - console.log("NODE: ", nodedata) - return; - } - const cytoscapeElement = document.getElementById("cytoscape_view") if (cytoscapeElement !== undefined && cytoscapeElement !== null) { cytoscapeElement.style.cursor = "pointer" } + sendStreamRequest({ + "item": "node", + "type": "hover", + "id": nodedata.id, + }) + + if (nodedata.finished === false) { + console.log("NODE UNFINISHED (hover in): ", nodedata) + + // Should just be 1, so this should be fast enough :3 + const incomingEdges = event.target.incomers("edge").jsons() + if (incomingEdges !== undefined && incomingEdges !== null) { + for (var i = 0; i < incomingEdges.length; i++) { + // Find the actual edge + const edge = cy.getElementById(incomingEdges[i].data.id) + if (edge === undefined || edge === null) { + console.log("edge is null or undefined") + continue + } + + // Set the edge to be dashed + edge.style("target-arrow-color", "white") + edge.style("line-style", "solid") + edge.style("line-gradient-stop-colors", ["white", "white"]) + } + } + + return; + } + + //var parentNode = cy.$("#" + event.target.data("id")); //if (parentNode.data("isButton") || parentNode.data("buttonId")) return; if (nodedata.app_name !== undefined && !workflow.public === true) { - const allNodes = cy.nodes().jsons(); + const allNodes = cy.nodes().jsons(); - if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") { - console.log("In this :)") + if (nodedata.app_name === "Webhook" || nodedata.app_name === "Schedule" || nodedata.app_name === "Gmail" || nodedata.app_name === "Office365") { + console.log("In this :)") - var found = false; - for (let nodekey in allNodes) { - const currentNode = allNodes[nodekey]; - if ( - currentNode.data.attachedTo === nodedata.id && - currentNode.data.isDescriptor - ) { - found = true; - console.log("FOUND THE NODE!"); - break; - } - } - - if (!found) { - console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions) - // Find how many executions it has - var executions = 0 - const matchingExecutions = workflowExecutions.filter((execution => execution.execution_source === nodedata.app_name.toLowerCase())) - console.log("Matches: ", matchingExecutions.length) - const color = matchingExecutions.length > 0 ? "#34a853" : "#ea4436" - const decoratorNode = { - position: { - x: event.target.position().x + 44, - y: event.target.position().y + 44, - }, - locked: true, - data: { - isDescriptor: true, - isValid: true, - is_valid: true, - isTrigger: true, - label: `${matchingExecutions.length}`, - attachedTo: nodedata.id, - imageColor: color, - hasExecutions: true, - }, - }; - - cy.add(decoratorNode) + var found = false; + for (let nodekey in allNodes) { + const currentNode = allNodes[nodekey]; + if ( + currentNode.data.attachedTo === nodedata.id && + currentNode.data.isDescriptor + ) { + found = true; + console.log("FOUND THE NODE!"); + break; } } + if (!found) { + console.log("Find amount of executions for the specific nodetype: ", nodedata.app_name, "Executions: ", workflowExecutions) + // Find how many executions it has + var executions = 0 + const matchingExecutions = workflowExecutions.filter((execution => execution.execution_source === nodedata.app_name.toLowerCase())) + console.log("Matches: ", matchingExecutions.length) + const color = matchingExecutions.length > 0 ? "#34a853" : "#ea4436" + const decoratorNode = { + position: { + x: event.target.position().x + 44, + y: event.target.position().y + 44, + }, + locked: true, + data: { + isDescriptor: true, + isValid: true, + is_valid: true, + isTrigger: true, + label: `${matchingExecutions.length}`, + attachedTo: nodedata.id, + imageColor: color, + hasExecutions: true, + }, + }; + + cy.add(decoratorNode) + } + } + var found = false; for (var _key in allNodes) { const currentNode = allNodes[_key]; // console.log("CURRENT NODE: ", currentNode) - if ( - (currentNode.data.isButton || currentNode.data.isSuggestion) && - currentNode.data.attachedTo !== nodedata.id - ) { + if ((currentNode.data.isButton || currentNode.data.isSuggestion) && currentNode.data.attachedTo !== nodedata.id) { cy.getElementById(currentNode.data.id).remove(); } @@ -4894,11 +5664,8 @@ const AngularWorkflow = (defaultprops) => { cy.getElementById(currentNode.data.id).remove(); }*/ - if ( - currentNode.data.isButton && - currentNode.data.attachedTo === nodedata.id - ) { - found = true; + if (currentNode.data.isButton && currentNode.data.attachedTo === nodedata.id) { + found = true; } } @@ -4909,19 +5676,23 @@ const AngularWorkflow = (defaultprops) => { if (nodedata.trigger_type === "SUBFLOW" || nodedata.trigger_type === "USERINPUT") { addCopyButton(event); } else { - // Check how many executions from the source - addRunCountButton(event); - } - } else { - addCopyButton(event); - addStartnodeButton(event); - } + // Check how many executions from the source + addRunCountButton(event); + } + } else { + addCopyButton(event); + addStartnodeButton(event); + } - // autocomplete - // right click - // suggestions - addSuggestionButtons(nodedata, event); - } + // autocomplete + // right click + // suggestions + if (workflow.actions.length < 4) { + addSuggestionButtons(nodedata, event); + } else { + console.log("Too many actions to suggest (for now)") + } + } } var parsedStyle = { @@ -4990,16 +5761,20 @@ const AngularWorkflow = (defaultprops) => { }; const onEdgeHoverOut = (event) => { - if (event === null || event === undefined) { + if (event === null || event === undefined || event.target === null || event.target === undefined) { event.target.removeStyle(); return; } const edgeData = event.target.data(); if (edgeData.decorator === true) { + + // Defaults + event.target.style("target-arrow-color", "#555555") + event.target.style("line-style", "dashed") + event.target.style("line-gradient-stop-colors", ["#555555", "#555555"]) + return; - - } const cytoscapeElement = document.getElementById("cytoscape_view") @@ -5012,15 +5787,27 @@ const AngularWorkflow = (defaultprops) => { // This is here to have a proper transition for lines const onEdgeHover = (event) => { - if (event === null || event === undefined) { + if (event === null || event === undefined || event.target === null || event.target === undefined) { return; } const edgeData = event.target.data(); if (edgeData.decorator === true) { + // Set color of it to white and not stripled + event.target.style("target-arrow-color", "white") + event.target.style("line-style", "solid") + event.target.style("line-gradient-stop-colors", ["white", "white"]) + return; } + // FIXME: Color problem. Do later + //sendStreamRequest({ + // "item": "edge", + // "type": "hover", + // "id": edgeData.id, + //}) + const cytoscapeElement = document.getElementById("cytoscape_view") if (cytoscapeElement !== undefined && cytoscapeElement !== null) { cytoscapeElement.style.cursor = "pointer" @@ -5070,6 +5857,14 @@ const AngularWorkflow = (defaultprops) => { //} if (event.target !== undefined && event.target !== null) { + + // If decorator and hovered + // Set color to white + + + + + //const targetcolor = "#66a8b1" //const parsedStyle = { // "width": "10px", @@ -5426,12 +6221,10 @@ const AngularWorkflow = (defaultprops) => { insertedNodes = insertedNodes.concat(newedges); - console.log("NODES: ", insertedNodes) setWorkflow(inputworkflow); // Reset view for cytoscape if (cy !== undefined && cy !== null) { - console.log("In cy add!") cy.add(insertedNodes); cy.fit(null, 200); } else { @@ -5481,12 +6274,11 @@ const AngularWorkflow = (defaultprops) => { if (selectedNode.data().decorator === true && selectedNode.data("type") !== "COMMENT") { toast("This node can't be deleted."); } else { - console.log("Deleted.") - selectedNode.remove(); + selectedNode.remove(); - setSelectedTrigger({}) - setSelectedEdge({}) - setSelectedAction({}) + setSelectedTrigger({}) + setSelectedEdge({}) + setSelectedAction({}) } // An attempt at NOT unselecting when removing @@ -5517,38 +6309,52 @@ const AngularWorkflow = (defaultprops) => { } const fetchRecommendations = (inputWorkflow) => { - const parsedWorkflow = JSON.parse(JSON.stringify(inputWorkflow)) + const parsedWorkflow = JSON.parse(JSON.stringify(inputWorkflow)) fetch(globalUrl + "/api/v1/workflows/recommend", { method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(parsedWorkflow), - credentials: "include", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(parsedWorkflow), + credentials: "include", }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for usecases"); - } + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for usecases"); + } - return response.json(); - }) - .then((responseJson) => { - if (responseJson.success !== false) { - console.log("recommendations: ", responseJson); + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success !== false) { + //console.log("recommendations: ", responseJson); - if (responseJson.actions !== undefined && responseJson.actions !== null) { - setWorkflowRecommendations(responseJson.actions) - } - } else { - } - }) - .catch((error) => { - //toast("ERROR: " + error.toString()); - console.log("ERROR getting usecases: " + error.toString()); - }) + if (responseJson.actions !== undefined && responseJson.actions !== null) { + console.log("Got recommendations: ", responseJson.actions) + + //if (cy !== undefined && cy !== null) { + // cy.removeListener("mouseover", "node"); + //} + + setWorkflowRecommendations(responseJson.actions) + + //if (cy !== undefined && cy !== null) { + // cy.on("mouseover", "node", (e) => onNodeHover(e)); + //} + } else { + setWorkflowRecommendations([]) + } + } else { + setWorkflowRecommendations([]) + } + }) + .catch((error) => { + //toast("ERROR: " + error.toString()); + setWorkflowRecommendations([]) + console.log("ERROR getting usecases: " + error.toString()); + }) } const fetchUsecases = () => { @@ -5659,13 +6465,13 @@ const AngularWorkflow = (defaultprops) => { } // App length necessary cus of cy initialization - if (elements.length === 0 && workflow.actions !== undefined && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) { + if (elements.length === 0 && workflow.actions !== undefined && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0 && workflowRecommendations !== undefined) { setGraphSetup(true); setupGraph(workflow); console.log("In graph setup") // 2nd load - configures cytoscape - } else if (!established && cy !== undefined && ((apps !== null && apps !== undefined && apps.length > 0) || workflow.public === true) && Object.getOwnPropertyNames(workflow).length > 0 && appAuthentication !== undefined) { + } else if (!established && cy !== undefined && ((apps !== null && apps !== undefined && apps.length > 0) || workflow.public === true) && Object.getOwnPropertyNames(workflow).length > 0 && appAuthentication !== undefined && workflowRecommendations !== undefined) { console.log("In POST graph setup!") @@ -5743,7 +6549,7 @@ const AngularWorkflow = (defaultprops) => { cy.on("boxstart", (e) => { console.log("START"); - cy.removeListener("select"); + //cy.removeListener("select"); }); cy.on("boxend", (e) => { @@ -5778,6 +6584,9 @@ const AngularWorkflow = (defaultprops) => { cy.on("cxttap", "node", (e) => onCtxTap(e)); document.title = "Workflow - " + workflow.name; + + startWorkflowStream(props.match.params.key); + registerKeys(); } //}) @@ -5973,7 +6782,7 @@ const AngularWorkflow = (defaultprops) => { marginLeft: "10px", marginTop: "5px", marginBottom: "5px", - width: "2px", + width: 2, backgroundColor: yellow, marginRight: "5px", }} @@ -6293,27 +7102,34 @@ const AngularWorkflow = (defaultprops) => {
{triggers.map((trigger, index) => { - var imageline = - trigger.large_image.length === 0 ? ( - - ) : ( + const imagesize = isMobile ? 40 : trigger.large_image.includes("svg") ? 80 : 80 + var imageline = trigger.large_image.length === 0 ? + : - ); - const title = trigger.trigger_type === "WEBHOOK" ? "Workflow starters" : trigger.trigger_type === "SUBFLOW" ? "Mid-Workflow" : "" + const title = trigger.trigger_type === "WEBHOOK" ? "Workflow starters" : trigger.trigger_type === "SUBFLOW" ? "Mid-Workflow" : "" const color = trigger.is_valid ? green : yellow; return ( - - {title.length > 0 ? - - {title} - - : null} + + {title.length > 0 ? + + {title} + + : null} { { }}>
{
-
+
); })}
@@ -6614,35 +7430,51 @@ const AngularWorkflow = (defaultprops) => { newNodeId = uuidv4(); const actionType = "ACTION"; const actionLabel = getNextActionName(app.name); - console.log("Next action name: ", actionLabel) + //console.log("Next action name: ", actionLabel) var parameters = null; var example = ""; var description = "" + const startIndex = app.actions.findIndex((action) => action.category_label !== undefined && action.category_label !== null && action.category_label.length > 0) + const actionIndex = startIndex < 0 ? 0 : startIndex + + // Make the first action the most relevant one for them based on previous use if ( - app.actions[0].parameters !== undefined && - app.actions[0].parameters !== null && - app.actions[0].parameters.length > 0 + app.actions[actionIndex].parameters !== undefined && + app.actions[actionIndex].parameters !== null && + app.actions[actionIndex].parameters.length > 0 ) { - parameters = app.actions[0].parameters; + parameters = app.actions[actionIndex].parameters; + for (let paramkey in parameters) { + // Check if parameter.name == "headers" and if it includes "=undefined". If it does, set the value to example if it exists, otherwise empty + if (parameters[paramkey].name === "headers" && parameters[paramkey].value.includes("=undefined")) { + if (parameters[paramkey].example !== undefined && parameters[paramkey].example !== null && parameters[paramkey].example.length > 0) { + parameters[paramkey].value = parameters[paramkey].example + } else { + parameters[paramkey].value = "" + } + } + } + + //parameters = app.actions[0].parameters; } if ( - app.actions[0].returns !== undefined && - app.actions[0].returns !== null && - app.actions[0].returns.example !== undefined && - app.actions[0].returns.example !== null && - app.actions[0].returns.example.length > 0 + app.actions[actionIndex].returns !== undefined && + app.actions[actionIndex].returns !== null && + app.actions[actionIndex].returns.example !== undefined && + app.actions[actionIndex].returns.example !== null && + app.actions[actionIndex].returns.example.length > 0 ) { - example = app.actions[0].returns.example; + example = app.actions[actionIndex].returns.example; } if ( - app.actions[0].description !== undefined && - app.actions[0].description !== null && - app.actions[0].description.length > 0 + app.actions[actionIndex].description !== undefined && + app.actions[actionIndex].description !== null && + app.actions[actionIndex].description.length > 0 ) { - description = app.actions[0].description + description = app.actions[actionIndex].description } const parsedEnvironments = @@ -6654,7 +7486,7 @@ const AngularWorkflow = (defaultprops) => { // activated: app.generated === true ? app.activated === false ? false : true : true, const newAppData = { - name: app.actions[0].name, + name: app.actions[actionIndex].name, label: actionLabel, app_name: app.name, app_version: app.app_version, @@ -6677,6 +7509,7 @@ const AngularWorkflow = (defaultprops) => { authentication: [], execution_variable: undefined, example: example, + required_body_fields: app.actions[actionIndex].required_body_fields, category: app.categories !== null && app.categories !== undefined && @@ -7315,7 +8148,6 @@ const AngularWorkflow = (defaultprops) => { if (allitems !== undefined && allitems !== null) { for (let itemkey in allitems) { const item = allitems[itemkey]; - console.log("Appname: ", appName, "Item: ", item); if (item.app_name === appName && item.label !== undefined && item.label !== null) { var number = item.label.split("_"); @@ -7327,9 +8159,6 @@ const AngularWorkflow = (defaultprops) => { } appName = appName.replaceAll(" ", "_") - - console.log("Highest:", highest) - if (highest) { return appName + "_" + (parseInt(highest) + 1); } else { @@ -7347,7 +8176,7 @@ const AngularWorkflow = (defaultprops) => { ); if (newaction === undefined || newaction === null) { - toast("Failed to find the action"); + toast("Failed to find the action you selected. Please try again or contact support@shuffler.io if it persists."); return; } @@ -7355,6 +8184,7 @@ const AngularWorkflow = (defaultprops) => { const foundInfo = workflow.actions.find(ac => ac.id === selectedAction.id) } + // Setting an old reference just to use the same memory space elsewhere // for selectedAction const oldaction = JSON.parse(JSON.stringify(selectedAction)) @@ -7367,81 +8197,95 @@ const AngularWorkflow = (defaultprops) => { newSelectedAction.errors = []; newSelectedAction.isValid = true; newSelectedAction.is_valid = true; - //console.log(newSelectedAction) + newSelectedAction.required_body_fields = newaction.required_body_fields + console.log("New selected action: ", newSelectedAction) - // Simmple action swap autocompleter - if (oldaction.parameters !== undefined && oldaction.parameters !== null && newSelectedAction.parameters !== undefined && oldaction.id === newSelectedAction.id) { - var fileid_found = false - for (let [paramkey,paramkeyval] in Object.entries(oldaction.parameters)) { - const param = oldaction.parameters[paramkey]; - - if (param.name === "file_id") { - fileid_found = true + // Simple action swap autocompleter + if (oldaction.parameters !== undefined && oldaction.parameters !== null && newSelectedAction.parameters !== undefined && oldaction.id === newSelectedAction.id) { + var fileid_found = false + + for (let [paramkey,paramkeyval] in Object.entries(oldaction.parameters)) { + const param = oldaction.parameters[paramkey]; + + if (param.name === "file_id") { + fileid_found = true + } + + if (param.value === null || param.value === undefined || param.value.length === 0) { + continue + } + + if (param.name === "body") { + //console.log("Param: ", param) + continue + } + + if (param.name === "headers") { + console.log("Swap header? For now, yes. File found: ", fileid_found) + + if (fileid_found) { + newSelectedAction.parameters[paramkey].value = "" + newSelectedAction.parameters[paramkey].autocompleted = true + + continue + } + //newSelectedAction.parameters[newParamIndex].value = param.value + } + + if (newSelectedAction.parameters === undefined || newSelectedAction.parameters === null) { + continue + } + + // Not doing options fields + const newParamIndex = newSelectedAction.parameters.findIndex(paramdata => paramdata.name === param.name) + if (newParamIndex < 0) { + continue + } + + if (newSelectedAction.parameters[newParamIndex].name === "headers") { + if (param.value !== undefined && param.value !== null && param.value.includes("=undefined")) { + if (newSelectedAction.parameters[newParamIndex].example !== undefined && newSelectedAction.parameters[newParamIndex].example !== null) { + newSelectedAction.parameters[newParamIndex].value = newSelectedAction.parameters[newParamIndex].example + } else { + newSelectedAction.parameters[newParamIndex].value = "" + } + + continue } + } - if (param.value === null || param.value === undefined || param.value.length === 0) { - continue - } + newSelectedAction.parameters[newParamIndex].value = param.value + newSelectedAction.parameters[newParamIndex].autocompleted = true + if (param.options !== undefined && param.options !== null && param.options.length > 0) { + newSelectedAction.parameters[newParamIndex].autocompleted = false + } + } + } - if (param.name === "body") { - //console.log("Param: ", param) - continue - } + if (newSelectedAction.app_name === "Shuffle Tools") { + const iconInfo = GetIconInfo(newSelectedAction); + console.log("ICONINFO: ", iconInfo); + const svg_pin = ``; + const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); + newSelectedAction.large_image = svgpin_Url; + newSelectedAction.fillGradient = iconInfo.fillGradient; + newSelectedAction.fillstyle = "solid"; + if ( + newSelectedAction.fillGradient !== undefined && + newSelectedAction.fillGradient !== null && + newSelectedAction.fillGradient.length > 0 + ) { + newSelectedAction.fillstyle = "linear-gradient"; + } else { + newSelectedAction.iconBackground = iconInfo.iconBackgroundColor; + } - if (param.name === "headers") { - console.log("Swap header? For now, yes. File found: ", fileid_found) - - if (fileid_found) { - newSelectedAction.parameters[paramkey].value = "" - newSelectedAction.parameters[paramkey].autocompleted = true - - continue - } - //newSelectedAction.parameters[newParamIndex].value = param.value - } - - if (newSelectedAction.parameters === undefined || newSelectedAction.parameters === null) { - continue - } - - // Not doing options fields - const newParamIndex = newSelectedAction.parameters.findIndex(paramdata => paramdata.name === param.name) - if (newParamIndex < 0) { - continue - } - - newSelectedAction.parameters[newParamIndex].value = param.value - newSelectedAction.parameters[newParamIndex].autocompleted = true - if (param.options !== undefined && param.options !== null && param.options.length > 0) { - newSelectedAction.parameters[newParamIndex].autocompleted = false - } - } - } - - if (newSelectedAction.app_name === "Shuffle Tools") { - const iconInfo = GetIconInfo(newSelectedAction); - console.log("ICONINFO: ", iconInfo); - const svg_pin = ``; - const svgpin_Url = encodeURI("data:image/svg+xml;utf-8," + svg_pin); - newSelectedAction.large_image = svgpin_Url; - newSelectedAction.fillGradient = iconInfo.fillGradient; - newSelectedAction.fillstyle = "solid"; - if ( - newSelectedAction.fillGradient !== undefined && - newSelectedAction.fillGradient !== null && - newSelectedAction.fillGradient.length > 0 - ) { - newSelectedAction.fillstyle = "linear-gradient"; - } else { - newSelectedAction.iconBackground = iconInfo.iconBackgroundColor; - } - - const foundnode = cy.getElementById(newSelectedAction.id); - if (foundnode !== null && foundnode !== undefined) { - foundnode.data(newSelectedAction); - } - } + const foundnode = cy.getElementById(newSelectedAction.id); + if (foundnode !== null && foundnode !== undefined) { + foundnode.data(newSelectedAction); + } + } // Takes an action as input, then runs through and updates the relevant parameters based on previous actions' results (parent nodes) @@ -7662,13 +8506,14 @@ const AngularWorkflow = (defaultprops) => { overflowAnchor: "none", }; + const minSize = 370 var rightsidebarStyle = { position: "fixed", top: appBarSize + 25, right: 25, height: "80vh", - width: isMobile ? "100%" : 365, - minWidth: 200, + width: isMobile ? "100%" : minSize, + minWidth: minSize, maxWidth: 600, maxHeight: "100vh", border: "1px solid rgb(91, 96, 100)", @@ -8482,7 +9327,7 @@ const AngularWorkflow = (defaultprops) => { marginLeft: "10px", marginTop: "5px", marginBottom: "5px", - width: "2px", + width: 2, backgroundColor: yellow, marginRight: "5px", }} @@ -8618,8 +9463,8 @@ const AngularWorkflow = (defaultprops) => { const conditionId = uuidv4(); return (
-
-

+
+

Branch: Conditions - {selectedEdgeIndex}

{
{triggerFolders.length === 0 ? - No folders found. Please authenticate and make sure the user has access folders available. If this persists, contact us. + No folders found. Please authenticate first. If this persists, contact us. : + + {errorText} + + + {circularLoader} + + + + + + ) : null + + + const modalView = openApiModal ? ( + { + setOpenApiModal(false); + setGenerateAppModal(false); + }} + PaperProps={{ + style: { + backgroundColor: surfaceColor, + color: "white", + minWidth: "800px", + minHeight: "320px", + }, + }} + > + + +
+ Create a new app from OpenAPI / Swagger
@@ -2671,6 +2935,7 @@ const Apps = (props) => {
{appView} {modalView} + {generateAppView} {appsModalLoad} {deleteModal}
From f99d458b9cdfd1c330071b26d1c7b20aa038b787 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 21 Sep 2023 18:00:02 +0200 Subject: [PATCH 043/160] Added organization deletion --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 4 +++- backend/go-app/walkoff.go | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 4647cfca..274bde57 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -//replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared go 1.19 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 26eb4760..fc2fe92f 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -6048,6 +6048,8 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/create_sub_org", shuffle.HandleCreateSubOrg).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org + r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS") + // This is a new API that validates if a key has been seen before. // Not sure what the best course of action is for it. r.HandleFunc("/api/v1/environments/{key}/stop", shuffle.HandleStopExecutions).Methods("GET", "POST", "OPTIONS") @@ -6057,8 +6059,8 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}/list_cache", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/get_cache", shuffle.HandleGetCacheKey).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/set_cache", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/orgs/{orgId}/stats", shuffle.HandleGetStatistics).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/orgs/{orgId}/statistics", shuffle.HandleGetStatistics).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/cache", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/cache", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 75c979dd..8f9e3273 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -992,6 +992,7 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { cacheKey := fmt.Sprintf("%s_workflows", user.Id) shuffle.DeleteCache(ctx, cacheKey) + shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.ActiveOrg.Id)) log.Printf("[DEBUG] Cleared workflow cache for %s (%s)", user.Username, user.Id) resp.WriteHeader(200) From fc9bc33b30e8f5bbddef525ad11bdde198654654 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 21 Sep 2023 20:18:22 +0200 Subject: [PATCH 044/160] Fixed self problems in app sdk --- backend/app_sdk/app_base.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 974f9dbb..6e5a56e7 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1294,6 +1294,27 @@ class AppBase: else: return returns + def delete_cache(self, key): + org_id = self.full_execution["workflow"]["execution_org"]["id"] + url = "%s/api/v1/orgs/%s/delete_cache" % (self.url, org_id, key) + + data = { + "workflow_id": self.full_execution["workflow"]["id"], + "execution_id": self.current_execution_id, + "authorization": self.authorization, + "org_id": org_id, + "key": key, + } + + response = requests.post(url, json=data, verify=False) + try: + allvalues = response.json() + return allvalues + except Exception as e: + self.logger.info("[ERROR} Failed to parse response from delete_cache: %s" % e) + #return response.json() + return {"success": False, "reason": f"Failed to delete cache for key {key}"} + def set_cache(self, key, value): org_id = self.full_execution["workflow"]["execution_org"]["id"] url = "%s/api/v1/orgs/%s/set_cache" % (self.url, org_id) @@ -1312,8 +1333,8 @@ class AppBase: allvalues["key"] = key allvalues["value"] = str(value) return allvalues - except: - self.logger.info("Value couldn't be parsed") + except Exception as e: + self.logger.info("[ERROR} Failed to parse response from set cache: %s" % e) #return response.json() return {"success": False} @@ -2283,7 +2304,10 @@ class AppBase: run = Liquid(template, mode="wild", from_file=False, filters=shuffle_filters.filters) # Can't handle self yet (?) - ret = run.render(**globals()) + all_globals = globals() + all_globals["self"] = self + + ret = run.render(**all_globals) return ret except jinja2.exceptions.TemplateNotFound as e: self.logger.info(f"[ERROR] Liquid Template error: {e}") From ec36639fa722ed64e12ea3226e6a8c621a8409b2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 22 Sep 2023 01:01:20 +0200 Subject: [PATCH 045/160] Fixed a bunch of minor issues with notification workflows and the like --- backend/app_sdk/app_base.py | 17 +- backend/app_sdk/requirements.txt | 2 +- frontend/src/App.jsx | 15 ++ frontend/src/components/OrgHeader.jsx | 1 - frontend/src/components/OrgHeaderexpanded.jsx | 234 +++++++++++++++--- frontend/src/components/ParsedAction.jsx | 2 +- frontend/src/views/Apps.jsx | 2 +- 7 files changed, 227 insertions(+), 46 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 6e5a56e7..98a5f8aa 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -22,8 +22,7 @@ import dateutil import threading import concurrent.futures -from io import StringIO as StringBuffer -from io import BytesIO +from io import StringIO as StringBuffer, BytesIO from liquid import Liquid, defaults runtime = os.getenv("SHUFFLE_SWARM_CONFIG", "") @@ -1296,7 +1295,7 @@ class AppBase: def delete_cache(self, key): org_id = self.full_execution["workflow"]["execution_org"]["id"] - url = "%s/api/v1/orgs/%s/delete_cache" % (self.url, org_id, key) + url = "%s/api/v1/orgs/%s/delete_cache" % (self.url, org_id) data = { "workflow_id": self.full_execution["workflow"]["id"], @@ -1309,11 +1308,11 @@ class AppBase: response = requests.post(url, json=data, verify=False) try: allvalues = response.json() - return allvalues + return json.dumps(allvalues) except Exception as e: self.logger.info("[ERROR} Failed to parse response from delete_cache: %s" % e) #return response.json() - return {"success": False, "reason": f"Failed to delete cache for key {key}"} + return json.dumps({"success": False, "reason": f"Failed to delete cache for key '{key}'"}) def set_cache(self, key, value): org_id = self.full_execution["workflow"]["execution_org"]["id"] @@ -2301,13 +2300,13 @@ class AppBase: #if len(template) > 100: # self.logger.info("[DEBUG] Running liquid with data of length %d" % len(template)) #self.logger.info(f"[DEBUG] Data: {template}") - run = Liquid(template, mode="wild", from_file=False, filters=shuffle_filters.filters) - # Can't handle self yet (?) all_globals = globals() - all_globals["self"] = self + all_globals["self"] = self + run = Liquid(template, mode="wild", from_file=False, filters=shuffle_filters.filters, globals=all_globals) - ret = run.render(**all_globals) + # Add locals that are missing to globals + ret = run.render() return ret except jinja2.exceptions.TemplateNotFound as e: self.logger.info(f"[ERROR] Liquid Template error: {e}") diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt index bacb3bc8..4f432bfc 100755 --- a/backend/app_sdk/requirements.txt +++ b/backend/app_sdk/requirements.txt @@ -1,7 +1,7 @@ urllib3==1.26.5 requests==2.25.1 MarkupSafe==2.0.1 -liquidpy==0.7.6 +liquidpy==0.8.1 flask[async]==2.0.2 waitress==2.1.0 #flask==1.1.2 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 28b665b0..2180be03 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -39,6 +39,9 @@ import AlertTemplate from "./components/AlertTemplate"; import { useAlert, positions, Provider } from "react-alert"; import { isMobile } from "react-device-detect"; +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; + import Drift from "react-driftjs"; // Production - backend proxy forwarding in nginx @@ -561,6 +564,18 @@ const App = (message, props) => { {includedData} + ); diff --git a/frontend/src/components/OrgHeader.jsx b/frontend/src/components/OrgHeader.jsx index 4ad76cc3..95020bb1 100644 --- a/frontend/src/components/OrgHeader.jsx +++ b/frontend/src/components/OrgHeader.jsx @@ -38,7 +38,6 @@ const OrgHeader = (props) => { handleEditOrg, } = props; - //const alert = useAlert(); const classes = useStyles(); var upload = ""; diff --git a/frontend/src/components/OrgHeaderexpanded.jsx b/frontend/src/components/OrgHeaderexpanded.jsx index 2d16effc..268350a2 100644 --- a/frontend/src/components/OrgHeaderexpanded.jsx +++ b/frontend/src/components/OrgHeaderexpanded.jsx @@ -1,8 +1,8 @@ import React, { useEffect } from "react"; import { makeStyles } from "@mui/styles"; -import { useAlert } from "react-alert"; import theme from '../theme.jsx'; +import { toast } from "react-toastify" import { FormControl, @@ -24,6 +24,7 @@ import { Tab, Grid, IconButton, + Autocomplete, } from "@mui/material"; import { @@ -48,7 +49,6 @@ const OrgHeaderexpanded = (props) => { adminTab, } = props; - const alert = useAlert(); const classes = useStyles(); const defaultBranch = "master"; @@ -155,6 +155,47 @@ const OrgHeaderexpanded = (props) => { : selectedOrganization.sso_config.openid_token ) + const [workflows, setWorkflows] = React.useState([]) + const [workflow, setWorkflow] = React.useState({}) + + const getAvailableWorkflows = (trigger_index) => { + fetch(globalUrl + "/api/v1/workflows", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for workflows :O!"); + return; + } + return response.json(); + }) + .then((responseJson) => { + if (responseJson !== undefined) { + setWorkflows(responseJson) + + if (selectedOrganization.defaults !== undefined && selectedOrganization.defaults.notification_workflow !== undefined) { + + const workflow = responseJson.find((workflow) => workflow.id === selectedOrganization.defaults.notification_workflow) + if (workflow !== undefined && workflow !== null) { + setWorkflow(workflow) + } + } + } + }) + .catch((error) => { + console.log("Error getting workflows: " + error); + }) + } + + useEffect(() => { + getAvailableWorkflows() + }, []) + const handleEditOrg = ( name, description, @@ -188,17 +229,29 @@ const OrgHeaderexpanded = (props) => { .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { - alert.error("Failed updating org: ", responseJson.reason); + toast("Failed updating org: ", responseJson.reason); } else { - alert.success("Successfully edited org!"); + toast("Successfully edited org!"); } }) ) .catch((error) => { - alert.error("Err: " + error.toString()); + toast("Err: " + error.toString()); }); }; + + const handleWorkflowSelectionUpdate = (e, isUserinput) => { + if (e.target.value === undefined || e.target.value === null || e.target.value.id === undefined) { + console.log("Returning as there's no id") + return null + } + + setWorkflow(e.target.value) + setNotificationWorkflow(e.target.value.id) + toast("Updated notification workflow. Don't forget to save!") + } + const orgSaveButton = (
@@ -247,34 +300,149 @@ const OrgHeaderexpanded = (props) => { - Notification Workflow ID - { - setNotificationWorkflow(e.target.value); - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> + Notification Workflow + {/* + + Add a Workflow that receives notifications from Shuffle when an error occurs in one of your workflows + + */} +
+ {workflows !== undefined && workflows !== null && workflows.length > 0 ? + { + if ( + option === undefined || + option === null || + option.name === undefined || + option.name === null + ) { + return "No Workflow Selected"; + } + + const newname = ( + option.name.charAt(0).toUpperCase() + option.name.substring(1) + ).replaceAll("_", " "); + return newname; + }} + options={workflows} + fullWidth + style={{ + backgroundColor: theme.palette.inputColor, + height: 50, + borderRadius: theme.palette.borderRadius, + }} + onChange={(event, newValue) => { + console.log("Found value: ", newValue) + + var parsedinput = { target: { value: newValue } } + + // For variables + if (typeof newValue === 'string' && newValue.startsWith("$")) { + parsedinput = { + target: { + value: { + "name": newValue, + "id": newValue, + "actions": [], + "triggers": [], + } + } + } + } + + handleWorkflowSelectionUpdate(parsedinput) + }} + renderOption={(props, data, state) => { + if (data.id === workflow.id) { + data = workflow; + } + + return ( + + {data.image !== undefined && data.image !== null && data.image.length > 0 ? + {data.name} + : null} + + Choose {data.name} + + + } placement="bottom"> + { + var parsedinput = { target: { value: data } } + handleWorkflowSelectionUpdate(parsedinput) + }} + > + {data.name} + + + ) + }} + renderInput={(params) => { + return ( + + ); + }} + /> + : + { + setNotificationWorkflow(e.target.value); + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + } +
+ {orgSaveButton} +
+
diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 209c7516..d963ec0b 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1131,7 +1131,7 @@ const ParsedAction = (props) => { borderRadius: theme.palette.borderRadius, }} onChange={(event, newValue) => { - console.log("SELECT: ", event, newValue) + console.log("SELECT: ", event, newValue) // Workaround with event lol //if (newValue !== undefined && newValue !== null) { // setNewSelectedAction({ target: { value: newValue.name } }); diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 4fd0e6bf..0e3868b0 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -2718,7 +2718,7 @@ const Apps = (props) => { - Paste in a URL, and we will make it into an app for you. This may take multiple minutes based on the size of the documentation. {isCloud ? "" : "Uses to Shuffle Cloud (https://shuffler.io) for processing."} + Paste in a URL, and we will make it into an app for you. This may take multiple minutes based on the size of the documentation. {isCloud ? "" : "Uses Shuffle Cloud (https://shuffler.io) for processing (for now)."} Date: Wed, 27 Sep 2023 20:58:08 +0200 Subject: [PATCH 046/160] Removed additional authentication handler for oauth2 --- backend/go-app/walkoff.go | 97 +-------------------------------------- 1 file changed, 1 insertion(+), 96 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 8f9e3273..d98f922b 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1188,7 +1188,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request var execution shuffle.ExecutionRequest err = json.Unmarshal(body, &execution) if err != nil { - log.Printf("[WARNING] Failed execution POST unmarshaling - continuing anyway: %s", err) + log.Printf("[WARNING] Failed execution POST unmarshalling for execution %s - continuing anyway: %s", execution.ExecutionId, err) //return shuffle.WorkflowExecution{}, "", err } @@ -1391,13 +1391,10 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request childNodes := shuffle.FindChildNodes(workflowExecution, workflowExecution.Start, []string{}, []string{}) - //topic := "workflows" startFound := false - // FIXME - remove this? newActions := []shuffle.Action{} defaultResults := []shuffle.ActionResult{} - allAuths := []shuffle.AppAuthenticationStorage{} for _, action := range workflowExecution.Workflow.Actions { //action.LargeImage = "" if action.ID == workflowExecution.Start { @@ -1409,98 +1406,6 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request return shuffle.WorkflowExecution{}, fmt.Sprintf("Environment is not defined for %s", action.Name), errors.New("Environment not defined!") } - // FIXME: Authentication parameters - if len(action.AuthenticationId) > 0 { - if len(allAuths) == 0 { - allAuths, err = shuffle.GetAllWorkflowAppAuth(ctx, workflow.ExecutingOrg.Id) - if err != nil { - log.Printf("Api authentication failed in get all app auth: %s", err) - return shuffle.WorkflowExecution{}, fmt.Sprintf("Api authentication failed in get all app auth: %s", err), err - } - } - - curAuth := shuffle.AppAuthenticationStorage{Id: ""} - for _, auth := range allAuths { - if auth.Id == action.AuthenticationId { - curAuth = auth - break - } - } - - if len(curAuth.Id) == 0 { - return shuffle.WorkflowExecution{}, fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId), errors.New(fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId)) - } - - if curAuth.Encrypted { - setField := true - newFields := []shuffle.AuthenticationStore{} - for _, field := range curAuth.Fields { - parsedKey := fmt.Sprintf("%s_%d_%s_%s", curAuth.OrgId, curAuth.Created, curAuth.Label, field.Key) - newValue, err := shuffle.HandleKeyDecryption([]byte(field.Value), parsedKey) - if err != nil { - log.Printf("[WARNING] Failed decryption for %s: %s", field.Key, err) - setField = false - break - } - - field.Value = string(newValue) - newFields = append(newFields, field) - } - - if setField { - curAuth.Fields = newFields - } - } else { - log.Printf("[INFO] AUTH IS NOT ENCRYPTED - attempting encrypting!") - err = shuffle.SetWorkflowAppAuthDatastore(ctx, curAuth, curAuth.Id) - if err != nil { - log.Printf("[WARNING] Failed running encryption during execution: %s", err) - } - } - - newParams := []shuffle.WorkflowAppActionParameter{} - if strings.ToLower(curAuth.Type) == "oauth2" { - log.Printf("[DEBUG] Should replace auth parameters (Oauth2)") - - for _, param := range curAuth.Fields { - if param.Key == "expiration" { - continue - } - - newParams = append(newParams, shuffle.WorkflowAppActionParameter{ - Name: param.Key, - Value: param.Value, - }) - } - - for _, param := range action.Parameters { - //log.Printf("Param: %#v", param) - if param.Configuration { - continue - } - - newParams = append(newParams, param) - } - } else { - // Rebuild params with the right data. This is to prevent issues on the frontend - for _, param := range action.Parameters { - - for _, authparam := range curAuth.Fields { - if param.Name == authparam.Key { - param.Value = authparam.Value - //log.Printf("Name: %s - value: %s", param.Name, param.Value) - //log.Printf("Name: %s - value: %s\n", param.Name, param.Value) - break - } - } - - newParams = append(newParams, param) - } - } - - action.Parameters = newParams - } - action.LargeImage = "" if len(action.Label) == 0 { action.Label = action.ID From bb14b192ee201f374059801e97214d9e18a0b6ad Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 27 Sep 2023 21:00:13 +0200 Subject: [PATCH 047/160] Force rebuild 1.2.1 of backend --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index bdc7b0d6..a6d4aa77 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: nightly + version: 1.2.1 experimental: true - app: app_sdk path: backend/app_sdk From c36dd35f17628aad222fefefc00a52c4eb95a764 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 27 Sep 2023 21:01:04 +0200 Subject: [PATCH 048/160] Rolling back to nightly --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index a6d4aa77..bdc7b0d6 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: 1.2.1 + version: nightly experimental: true - app: app_sdk path: backend/app_sdk From 29e8106670be8fbfbac7c47a88bd0ce2fcc5c907 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 27 Sep 2023 21:25:01 +0200 Subject: [PATCH 049/160] Added error messages --- frontend/src/views/AngularWorkflow.jsx | 197 +++++++++++++++---------- 1 file changed, 117 insertions(+), 80 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 3b3bab17..e8ccdde7 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -80,6 +80,7 @@ import { Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, + Warning as WarningIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, @@ -453,10 +454,8 @@ const AngularWorkflow = (defaultprops) => { const [lastExecution, setLastExecution] = React.useState(""); const [configureWorkflowModalOpen, setConfigureWorkflowModalOpen] = React.useState(false); - const curpath = - typeof window === "undefined" || window.location === undefined - ? "" - : window.location.pathname; + const curpath = typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname; + // 0 = normal, 1 = just done, 2 = normal const [savingState, setSavingState] = React.useState(0); @@ -925,7 +924,7 @@ const AngularWorkflow = (defaultprops) => { }; const getWorkflowExecution = (id, execution_id) => { - fetch(globalUrl + "/api/v1/workflows/" + id + "/executions", { + fetch(`${globalUrl}/api/v1/workflows/${id}/executions`, { method: "GET", headers: { "Content-Type": "application/json", @@ -941,38 +940,38 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - if (responseJson.length > 0) { + if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) { // FIXME: Sort this by time // - means it's opposite const newkeys = sortByKey(responseJson, "-started_at"); setWorkflowExecutions(newkeys); - const cursearch = - typeof window === "undefined" || window.location === undefined - ? "" - : window.location.search; + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; var tmpView = new URLSearchParams(cursearch).get("execution_id"); - if ( - execution_id !== undefined && execution_id !== null && - execution_id.length > 0 && (tmpView === undefined || tmpView === null || tmpView.length === 0) - ) { + if (execution_id !== undefined && execution_id !== null && execution_id.length > 0 && (tmpView === undefined || tmpView === null || tmpView.length === 0)) { tmpView = execution_id; } + // Compare with currently selected item if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) { + // Don't clean up if it's already open + if (executionModalOpen === true) { + return + } + const execution = responseJson.find((data) => data.execution_id === tmpView); + setExecutionModalOpen(true) if (execution !== null && execution !== undefined) { - - if (execution.execution_argument.includes("too large")) { - setExecutionData({}); - setExecutionRunning(true); - setExecutionRequestStarted(false); - } else { - setExecutionData(execution); - } + if (execution.execution_argument.includes("too large")) { + setExecutionData({}); + setExecutionRunning(true); + setExecutionRequestStarted(false); + } else { + setExecutionData(execution); + } setExecutionModalView(1); start(); @@ -1218,10 +1217,15 @@ const AngularWorkflow = (defaultprops) => { // Doesn't work because this is some async garbage if (executionData.execution_id === undefined || (responseJson.execution_id === executionData.execution_id && responseJson.results !== undefined && responseJson.results !== null)) { if (executionData.status !== responseJson.status || executionData.result !== responseJson.result || (executionData.results !== undefined && responseJson.results !== null && executionData.results.length !== responseJson.results.length)) { - console.log("Updating data!") + console.log("Updating data!") setExecutionData(responseJson) } else { - console.log("NOT updating executiondata state."); + if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE" || responseJson.status === "WAITING") { + stop() + } + + //console.log("NOT updating executiondata state."); + return } } } @@ -1275,8 +1279,8 @@ const AngularWorkflow = (defaultprops) => { }; const sendStreamRequest = (body) => { - //console.log("Stream not activated yet.") - //return + console.log("Stream not activated yet.") + return // Session may be important here huh body.user_id = userdata.id @@ -1572,19 +1576,19 @@ const AngularWorkflow = (defaultprops) => { workflow.isValid = true; workflow.is_valid = true; - const cyelements = cy.elements(); - - for (let i = 0; i < cyelements.length; i++) { - //cyelements[i].removeStyle(); - cyelements[i].data().is_valid = true; - cyelements[i].data().errors = []; - } + const cyelements = cy.elements(); + + for (let i = 0; i < cyelements.length; i++) { + //cyelements[i].removeStyle(); + cyelements[i].data().is_valid = true; + cyelements[i].data().errors = []; + } - for (let actionkey in workflow.actions) { - workflow.actions[actionkey].is_valid = true; - workflow.actions[actionkey].errors = []; - } - } + for (let actionkey in workflow.actions) { + workflow.actions[actionkey].is_valid = true; + workflow.actions[actionkey].errors = []; + } + } setWorkflow(workflow); } @@ -1661,8 +1665,7 @@ const AngularWorkflow = (defaultprops) => { } const data = { execution_argument: executionArgument, start: startNode }; - fetch( - globalUrl + "/api/v1/workflows/" + props.match.params.key + "/execute", + fetch(`${globalUrl}/api/v1/workflows/${props.match.params.key}/execute`, { method: "POST", headers: { @@ -1795,8 +1798,8 @@ const AngularWorkflow = (defaultprops) => { item.fields = newfields; - const appname = selectedApp.name.toLowerCase().replace(" ", "_", -1) - const itemname = item.app.name.toLowerCase().replace(" ", "_", -1) + const appname = selectedApp.name.toLowerCase().replaceAll(" ", "_", -1) + const itemname = item.app.name.toLowerCase().replaceAll(" ", "_", -1) if (itemname === appname) { authenticationOptions.push(item); @@ -1806,7 +1809,7 @@ const AngularWorkflow = (defaultprops) => { selectedAction.selectedAuthentication = item; for (let actionkey in workflow.actions) { - const actionAppname = workflow.actions[actionkey].app_name.toLowerCase().replace(" ", "_", -1) + const actionAppname = workflow.actions[actionkey].app_name.toLowerCase().replaceAll(" ", "_", -1) if (actionAppname === appname) { workflow.actions[actionkey].selectedAuthentication = item; workflow.actions[actionkey].authentication_id = item.id; @@ -2536,6 +2539,8 @@ const AngularWorkflow = (defaultprops) => { const startWorkflowStream = async (workflowId) => { const timeout = 60000 + + return while (true) { // Wait 1 second before next request just in case of timeouts @@ -6409,8 +6414,6 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - console.log("Got workflow revisions: ", responseJson) - if (responseJson.success === false) { console.log("Error getting workflow revisions: ", responseJson) return @@ -6432,10 +6435,7 @@ const AngularWorkflow = (defaultprops) => { getApps(); fetchUsecases() - const cursearch = - typeof window === "undefined" || window.location === undefined - ? "" - : window.location.search; + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; // FIXME: Don't check specific one here const tmpExec = new URLSearchParams(cursearch).get("execution_highlight"); @@ -8585,10 +8585,18 @@ const AngularWorkflow = (defaultprops) => { }; const setTriggerOptionsWrapper = (value) => { - if (selectedTrigger.parameters === null) { - selectedTrigger.parameters = []; + if (selectedTrigger.parameters === null || selectedTrigger.parameters === undefined) { + selectedTrigger.parameters = [ + { name: "", value: "" }, + { name: "", value: "" }, + { name: "", value: "" }, + ] } + if (selectedTrigger.parameters.length < 3 ) { + selectedTrigger.parameters.push({ name: "", value: "" }) + } + const splitItems = workflow.triggers[selectedTriggerIndex].parameters[2].value.split(","); console.log(splitItems); @@ -12292,10 +12300,7 @@ const AngularWorkflow = (defaultprops) => { }; const UserinputSidebar = () => { - if ( - Object.getOwnPropertyNames(selectedTrigger).length > 0 && - workflow.triggers[selectedTriggerIndex] !== undefined - ) { + if (Object.getOwnPropertyNames(selectedTrigger).length > 0 && workflow.triggers[selectedTriggerIndex] !== undefined) { if ( workflow.triggers[selectedTriggerIndex].parameters === undefined || workflow.triggers[selectedTriggerIndex].parameters === null || @@ -12440,7 +12445,7 @@ const AngularWorkflow = (defaultprops) => { rows="4" multiline defaultValue={ - workflow.triggers[selectedTriggerIndex].parameters[0].value + workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers[selectedTriggerIndex].parameters !== undefined && workflow.triggers[selectedTriggerIndex].parameters.length > 0 && workflow.triggers[selectedTriggerIndex].parameters[0] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[0].value !== undefined ? workflow.triggers[selectedTriggerIndex].parameters[0].value : "" } color="primary" placeholder="defaultValue" @@ -12490,7 +12495,7 @@ const AngularWorkflow = (defaultprops) => { 0 && workflow.triggers[selectedTriggerIndex].parameters[2] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[2].value !== undefined ? workflow.triggers[selectedTriggerIndex].parameters[2].value.includes("sms") : false} onChange={() => { setTriggerOptionsWrapper("sms"); }} @@ -13272,12 +13277,45 @@ const AngularWorkflow = (defaultprops) => { ) } + + const showErrors = !isMobile && !workflow.public && workflow.errors !== undefined && workflow.errors !== null && workflow.errors.length > 0 ? +
+ + + {workflow.errors.length} Potential Workflow Issue{workflow.errors.length > 1 ? "s" : ""} + + + {workflow.errors.slice(0,4).map((error) => { + return ( +
+ - {error} +
+ ) + })} +
+
+ : null + const BottomCytoscapeBar = () => { if (workflow.id === undefined || workflow.id === null || (!workflow.public && apps.length === 0)) { return null; } const boxSize = isMobile ? 50 : 100; + + const executionButton = executionRunning ? ( @@ -14654,18 +14692,14 @@ const AngularWorkflow = (defaultprops) => { onMouseOver={() => { }} onMouseOut={() => { }} onClick={() => { - if ( - (data.result === undefined || - data.result === null || - data.result.length === 0) && - data.status !== "FINISHED" && - data.status !== "ABORTED" - ) { + if ((data.result === undefined || data.result === null || data.result.length === 0) && data.status !== "FINISHED" && data.status !== "ABORTED") { start(); setExecutionRunning(true); setExecutionRequestStarted(false); } + navigate(`?execution_id=${data.execution_id}`) + // Ensuring we have the latest version of the result. // Especially important IF the result is > 1 Mb in cloud @@ -14708,22 +14742,20 @@ const AngularWorkflow = (defaultprops) => { if (!checkStarted) { handleUpdateResults(data, cur_execution); - console.log("Clearing colors during click for: !", data) + if (cy !== undefined && cy !== null) { + cy.elements().removeClass("success-highlight failure-highlight executing-highlight"); + for (let actionKey in data.workflow.actions) { + var actionitem = data.workflow.actions[actionKey]; - if (cy !== undefined && cy !== null) { - cy.elements().removeClass("success-highlight failure-highlight executing-highlight"); - for (let actionKey in data.workflow.actions) { - var actionitem = data.workflow.actions[actionKey]; + handleColoring(actionitem.id, "", actionitem.label) + } - handleColoring(actionitem.id, "", actionitem.label) - } + for (let resultKey in data.results) { + var item = data.results[resultKey]; - for (let resultKey in data.results) { - var item = data.results[resultKey]; - - handleColoring(item.action.id, item.status, item.action.label) - } - } + handleColoring(item.action.id, item.status, item.action.label) + } + } setExecutionData(data); } @@ -14848,7 +14880,11 @@ const AngularWorkflow = (defaultprops) => {

{ }} + onClick={() => { + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; + const newitem = removeParam("execution_id", cursearch); + navigate(curpath + newitem) + }} > See more runs

@@ -15173,7 +15209,7 @@ const AngularWorkflow = (defaultprops) => { /> ); } else { - console.log("Node not found: ", nodedata) + //console.log("Node not found: ", nodedata) actionimg = ( {data.action.app_name} { {showWorkflowRevisions ? null : {/**/} + {showErrors} @@ -16434,7 +16471,7 @@ const AngularWorkflow = (defaultprops) => {
- Authentication for {selectedApp.name.replace("_", " ", -1)} + Authentication for {selectedApp.name.replaceAll("_", " ", -1)}
From 787ca47add3417c70f03f4dcfd100da8043161e7 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 27 Sep 2023 21:25:45 +0200 Subject: [PATCH 050/160] Added further admin panel fixes --- backend/app_sdk/app_base.py | 24 +- backend/go-app/go.mod | 4 +- frontend/src/components/Appsearch.jsx | 49 +--- frontend/src/components/Branding.jsx | 126 ++++++++-- frontend/src/components/Header.jsx | 1 - frontend/src/components/Priority.jsx | 2 +- frontend/src/views/Admin.jsx | 17 +- frontend/src/views/AppCreator.jsx | 328 +++++++++++++++++++++++--- frontend/src/views/Apps.jsx | 3 +- frontend/src/views/SettingsPage.jsx | 42 ++-- frontend/src/views/Workflows.jsx | 28 ++- functions/onprem/orborus/orborus.go | 4 +- 12 files changed, 474 insertions(+), 154 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 98a5f8aa..d22bc865 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1,11 +1,12 @@ import os import ast -import copy import sys import re +import copy import time import base64 import json +import random import liquid import logging import urllib3 @@ -504,10 +505,12 @@ class AppBase: except Exception as e: print(f"[WARNING] Failed adding parameter for logs: {e}") - # FIXME: Adding retries here. try: finished = False for i in range (0, 10): + # Random sleeptime between 0 and 1 second, with 0.1 increments + sleeptime = float(random.randint(0, 10) / 10) + try: ret = requests.post(url, headers=headers, json=action_result, timeout=10, verify=False) @@ -516,29 +519,29 @@ class AppBase: finished = True break else: - self.logger.info(f"[ERROR] RESP: {ret.text}") + self.logger.info(f"[ERROR] Bad resp {ret.status_code}: {ret.text}") except requests.exceptions.RequestException as e: self.logger.info(f"[DEBUG] Request problem: {e}") - time.sleep(0.1) + time.sleep(sleeptime) #time.sleep(5) continue except TimeoutError as e: self.logger.info(f"[DEBUG] Timeout or request: {e}") - time.sleep(0.1) + time.sleep(sleeptime) #time.sleep(5) continue except requests.exceptions.ConnectionError as e: self.logger.info(f"[DEBUG] Connectionerror: {e}") - time.sleep(0.1) + time.sleep(sleeptime) #time.sleep(5) continue except http.client.RemoteDisconnected as e: self.logger.info(f"[DEBUG] Remote: {e}") - time.sleep(0.1) + time.sleep(sleeptime) #time.sleep(5) continue @@ -555,8 +558,11 @@ class AppBase: # Not sure why this would work tho :) action_result["status"] = "FAILURE" action_result["result"] = json.dumps({"success": False, "reason": "POST error: Failed connecting to %s over 10 retries to the backend" % url}) - self.logger.info(f"[DEBUG] Before typeerror stream result - NOT finished after 10 requests") - ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result, verify=False) + self.logger.info(f"[ERROR] Before typeerror stream result - NOT finished after 10 requests") + + #ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result, verify=False) + self.send_result(action_result, {"Content-Type": "application/json", "Authorization": "Bearer %s" % self.authorization}, "/api/v1/streams") + return self.logger.info(f"""[DEBUG] Successful request result request: Status= {ret.status_code} & Response= {ret.text}. Action status: {action_result["status"]}""") except requests.exceptions.ConnectionError as e: diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 274bde57..abb8c18e 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared +//replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared go 1.19 @@ -19,7 +19,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.4.41 + github.com/shuffle/shuffle-shared v0.4.47 golang.org/x/crypto v0.9.0 google.golang.org/api v0.125.0 google.golang.org/appengine v1.6.7 diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index 090dc620..7408dad5 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -22,10 +22,9 @@ import { import aa from 'search-insights' const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const Appsearch = props => { - const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList} = props + const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - //const alert = useAlert(); const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs //const theme = useTheme(); @@ -41,45 +40,6 @@ const Appsearch = props => { const borderRadius = 3 window.title = "Shuffle | Apps | Find and integration any app" - const setUserSpecialzedApp = (user, data) => { - // var data = newfields] - console.log("data value", data) - const appData = {"user_id":user,"specialized_apps":[{}]} - console.log("User Check for appdata:", user) - appData["specialized_apps"][0]["name"] = data["name"] - appData["specialized_apps"][0]["image"] = data["image_url"] - appData["specialized_apps"][0]["category"] = data["categories"].toString() - console.log("AppData:",appData) - console.log("setActionImageList",setActionImageList) - console.log("actionImageList",actionImageList) - - const finalData = actionImageList.concat(appData["specialized_apps"]) - appData["specialized_apps"]=finalData - fetch(globalUrl + "/api/v1/users/updateuser", { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(appData), - credentials: "include", - }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for set creator :O!"); - } - toast("Sucessfully updated specialzed app.") - return response.json(); - }) - .then((responseJson) => { - if (!responseJson.success && responseJson.reason !== undefined) { - toast("Failed updating user: " + responseJson.reason); - } - }) - .catch((error) => { - console.log(error); - }); - }; // value={currentRefinement} const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { @@ -180,13 +140,8 @@ const Appsearch = props => { setMouseHoverIndex(-1) }} onClick={() => { if(isCreatorPage === true){ - console.log("data:",data) - console.log("userdata.id",userdata.id) - console.log("is creator", isCreatorPage) - if (setNewSelectedApp !== undefined) { - // setUserSpecialzedApp = data + if (setNewSelectedApp !== undefined && setUserSpecialzedApp !== undefined) { setUserSpecialzedApp(userdata.id, data) - //setActionImageList(userdata.id, data) } } if (setNewSelectedApp !== undefined) { diff --git a/frontend/src/components/Branding.jsx b/frontend/src/components/Branding.jsx index e565a013..9dd40791 100644 --- a/frontend/src/components/Branding.jsx +++ b/frontend/src/components/Branding.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import ReactGA from 'react-ga4'; import theme from "../theme.jsx"; +import { ToastContainer, toast } from "react-toastify" import { Paper, @@ -14,22 +15,87 @@ import { //import { useAlert const Branding = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; - //const alert = useAlert(); - const [publishingInfo, setPublishingInfo] = useState(""); + const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; + //const alert = useAlert(); + const [publishingInfo, setPublishingInfo] = useState(""); + const [publishRequirements, setPublishRequirements] = useState([]) - // Should enable / disable org branding - const handleChangePublishing = () => { - console.log("Handle change publishing"); - } + + const handleEditOrg = (joinStatus) => { + const data = { + "org_id": selectedOrganization.id, + "creator_config": joinStatus, + }; + + const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; + 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 updating org: ", responseJson.reason); + } else { + if (joinStatus == "join") { + setPublishingInfo("Your organization is now part of the Creator Incentive Program. You can now create and publish content to your organization's page. You can also create a creator account to manage your organization's content.") + } else { + setPublishingInfo("Your organization is no longer part of the Creator Incentive Program. You can still create a creator account to manage your organization's content.") + } + handleGetOrg(selectedOrganization.id); + } + }) + ) + .catch((error) => { + toast("Err: " + error.toString()); + }); + }; + + // Should enable / disable org branding + const handleChangePublishing = () => { + console.log("Handle change publishing"); + + if (selectedOrganization.creator_id == "") { + handleEditOrg("join") + } else { + handleEditOrg("leave") + } + } const isOrganizationReady = () => { + console.log("Is organization ready?") + // A simple checklist to ensure the button shows up properly if (selectedOrganization.name === selectedOrganization.org) { + const comment = "Change the name of your organization" + if (!publishRequirements.includes(comment)) { + setPublishRequirements([...publishRequirements, comment]) + } + + return false; + } + + // Check if it's a suborg + if (selectedOrganization.creator_org !== "") { + const comment = "Child orgs can't become creators" + if (!publishRequirements.includes(comment)) { + setPublishRequirements([...publishRequirements, comment]) + } return false; } if (selectedOrganization.large_image === "" || selectedOrganization.large_image === theme.palette.defaultImage) { + const comment = "Add a logo for your organization" + if (!publishRequirements.includes(comment)) { + setPublishRequirements([...publishRequirements, comment]) + } return false; } @@ -38,40 +104,58 @@ const Branding = (props) => { return (
- - Branding - +

+ Branding +

You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more.

- Creator Network + Creator Incentive Program

-
+
- By changing publishing settings, you agree to our Terms of Service, and acknowledge that your organization's non-sensitive data will be turned into a creator account. Support: support@shuffler.io - + By changing publishing settings, you agree to our Terms of Service, and acknowledge that your organization's non-sensitive data will be added as a creator account. None of your existing workflows, apps, or other stored data will be published. Any admin in your organization can manage the creator configuration. Becoming a creator organization is reversible.
Support: support@shuffler.io + {selectedOrganization.creator_id == "" ? + +   + + : + + + Modify your creator organization + + } + - + {publishingInfo} + + {publishRequirements.map((item) => { + return ( +
+ Required: {item} +
+ ) + })} +
diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 8ee71450..5f66af70 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -823,7 +823,6 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho color: "white", height: 45, width: 85, - zIndex: 14999, }} value={userdata.active_org.id} diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 046c7fd9..4559246a 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -70,7 +70,7 @@ const Priority = (props) => { return ( -
+
{priority.type === "usecase" || priority.type == "apps" ? : null} diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 3e54131a..c8269a52 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -749,7 +749,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user image: image, defaults: defaults, sso_config: sso_config, - lead_info: lead_info, + lead_info: lead_info, }; const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; @@ -2371,7 +2371,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user */} {userdata.support === true ? - + {/**/}
); @@ -5360,6 +5447,167 @@ const AppCreator = (defaultprops) => {
) : null; + const validateRemote = () => { + setValidation(true); + + fetch(globalUrl + "/api/v1/get_openapi_uri", { + method: "POST", + headers: { + Accept: "application/json", + }, + body: JSON.stringify(openApi), + credentials: "include", + }) + .then((response) => { + setValidation(false); + if (response.status !== 200) { + return response.json(); + } + + return response.text(); + }) + .then((responseJson) => { + if (typeof responseJson !== "string" && !responseJson.success) { + console.log(responseJson.reason); + if (responseJson.reason !== undefined) { + setOpenApiError(responseJson.reason); + } else { + setOpenApiError("Undefined issue with OpenAPI validation"); + } + return; + } + + console.log("Validating response!"); + validateOpenApi(responseJson); + }) + .catch((error) => { + toast(error.toString()); + setOpenApiError(error.toString()); + }); + } + + const circularLoader = validation ? ( + + ) : null; + + const newApimodalView = openApiModal ? + { + setOpenApiModal(false) + }} + PaperProps={{ + style: { + backgroundColor: surfaceColor, + color: "white", + minWidth: "800px", + minHeight: "320px", + }, + }} + > + + +
+ Merge with another OpenAPI document. You will get to choose Actions before they are merged. +
+
+ + Paste in the URI for the OpenAPI + 0} + color="primary" + onClick={() => { + setOpenApiError(""); + validateRemote(); + }} + > + Validate + + ), + }} + onChange={(e) => { + setOpenApi(e.target.value); + }} + helperText={ + + Must point to a version 2 or 3 OpenAPI specification. + + } + placeholder="OpenAPI URI" + fullWidth + /> + {/* +
+ Example: +
+ https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/uber.json + */} +

Or upload a YAML/JSON specification

+ + + {errorText} + + + {circularLoader} + + + + +
+ : null + // Random names for type & autoComplete. Didn't research :^) const landingpageDataBrowser = (
@@ -5391,9 +5639,30 @@ const AppCreator = (defaultprops) => { onChange={editHeaderImage} /> -

- General information -

+
+
+

+ General information +

+
+
+ + { + setOpenApiModal(true) + }} + > + { + setOpenApiModal(true) + }} + /> + + +
+
{ isLoaded && isAppLoaded ? (
{landingpageDataBrowser}
+ {newApimodalView}
) : (
diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 0e3868b0..218f9e39 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -1354,7 +1354,7 @@ const Apps = (props) => { border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", cursor: hover ? "pointer" : "default", textAlign: "center", - height: 150, + height: 125, }} > {icon} @@ -2556,6 +2556,7 @@ const Apps = (props) => { const circularLoader = validation ? ( ) : null; + const appsModalLoad = loadAppsModalOpen ? ( {

{passwordFormMessage}

-

Platform Earnings

+

Creator Incentive Program

-
- {isCloud ? - - - By connecting your Github account, you agree to our Terms of Service, and acknowledge that your non-sensitive data will be turned into a creator account. This enables you to earn a passive income from Shuffle. This IS reversible. Support: support@shuffler.io - - - - : null} -
+
+ {isCloud ? + + + By joining the Creator Incentive Program and connecting your Github account, you agree to our Terms of Service, and acknowledge that your non-sensitive data will be turned into a creator account. This enables you to earn a passive income from Shuffle. This IS reversible. Support: support@shuffler.io + + + + : null} +
{userdata.eth_info !== undefined && diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 73a4e68c..476dd9fc 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -638,7 +638,7 @@ const Workflows = (props) => { window.location.host === "shuffler.io"; const findWorkflow = (filters) => { - console.log("Using filters: ", filters) + console.log("Using filters: ", filters) if (filters.length === 0) { setFilteredWorkflows(workflows); handleKeysetting(allUsecases, workflows) @@ -656,6 +656,12 @@ const Workflows = (props) => { ); } + if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.length > 0) { + // Make them all lowercase + curWorkflow.tags = curWorkflow.tags.map((tag) => tag.toLowerCase()) + } + + if (found.every((v) => v !== true)) { found = filters.map((filter) => { if (filter === undefined || filter === null) { @@ -666,7 +672,7 @@ const Workflows = (props) => { if (curWorkflow.name.toLowerCase().includes(filter.toLowerCase())) { return true; - } else if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.includes(filter)) { + } else if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.includes(filter.toLowerCase())) { return true; } else if (curWorkflow.owner === filter) { return true; @@ -674,17 +680,17 @@ const Workflows = (props) => { return true; } else if (curWorkflow.usecase_ids !== undefined && curWorkflow.usecase_ids !== null && curWorkflow.usecase_ids.length > 0) { // Check if the usecase is the right category - for (var key in usecases) { - if (usecases[key].name.toLowerCase() !== newfilter) { - continue - } + for (var key in usecases) { + if (usecases[key].name.toLowerCase() !== newfilter) { + continue + } - for (var subkey in usecases[key].list) { - if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) { - return true - } - } + for (var subkey in usecases[key].list) { + if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) { + return true } + } + } } else if ( curWorkflow.actions !== null && curWorkflow.actions !== undefined diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 21e42e9e..2088ad28 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -224,7 +224,7 @@ func deployServiceWorkers(image string) { } if err != nil { - log.Printf("[ERROR] Failed to convert the default MTU to int: %s. Using 1500 instead", err) + log.Printf("[DEBUG] Failed to convert the default MTU to int: %s. Using 1500 instead", err) mtu = 1500 } @@ -1601,6 +1601,6 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error { _ = body - log.Printf("[DEBUG] Ran worker from request with execution ID: %s. Worker URL: %s. DEBUGGING: docker service logs shuffle-workers 2&>1 | grep %s", workflowExecution.ExecutionId, streamUrl, workflowExecution.ExecutionId) + log.Printf("[DEBUG] Ran worker from request with execution ID: %s. Worker URL: %s. DEBUGGING: docker service logs shuffle-workers 2>&1 | grep %s", workflowExecution.ExecutionId, streamUrl, workflowExecution.ExecutionId) return nil } From 890b13a08a8a55c4d01afc52ab250b6fbdd962ea Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 27 Sep 2023 21:26:25 +0200 Subject: [PATCH 051/160] Update dockerbuild.yaml --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index bdc7b0d6..a6d4aa77 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: nightly + version: 1.2.1 experimental: true - app: app_sdk path: backend/app_sdk From c421f6c30baf44e3ba10d48f66f53c786e6b02b4 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 27 Sep 2023 21:26:41 +0200 Subject: [PATCH 052/160] Rollback --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index a6d4aa77..bdc7b0d6 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: 1.2.1 + version: nightly experimental: true - app: app_sdk path: backend/app_sdk From 921bcb46930653b24400c47c87af6be31f2994ab Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 13:26:23 +0200 Subject: [PATCH 053/160] Fixed a SSO problem, and removed slow workflow loading without internet --- backend/go-app/go.mod | 2 +- frontend/src/components/Priority.jsx | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index abb8c18e..d6e77d3c 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -19,7 +19,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.4.47 + github.com/shuffle/shuffle-shared v0.4.48 golang.org/x/crypto v0.9.0 google.golang.org/api v0.125.0 google.golang.org/appengine v1.6.7 diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 4559246a..5e1c1943 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -21,8 +21,7 @@ import { //import { useAlert const Priority = (props) => { - const { globalUrl, userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, } = props; - + const { globalUrl, userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, } = props; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; let navigate = useNavigate(); @@ -52,15 +51,15 @@ const Priority = (props) => { }) .then((responseJson) => { if (responseJson.success === true) { - if (checkLogin !== undefined) { - checkLogin() - } + if (checkLogin !== undefined) { + checkLogin() + } } else { if (responseJson.success === false && responseJson.reason !== undefined) { - toast("Failed change recommendation: ", responseJson.reason) + toast("Failed change recommendation: ", responseJson.reason) } else { - toast("Failed change recommendation"); - } + toast("Failed change recommendation"); + } } }) .catch((error) => { @@ -70,7 +69,7 @@ const Priority = (props) => { return ( -
+
{priority.type === "usecase" || priority.type == "apps" ? : null} From 7dbe79ffbe8d383d5e4f614dbeea0a504be35542 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 13:59:12 +0200 Subject: [PATCH 054/160] 1.2.1 autobuild fix for specific backend issues --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index bdc7b0d6..a6d4aa77 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: nightly + version: 1.2.1 experimental: true - app: app_sdk path: backend/app_sdk From 15777637bc64b6f6f39a0b1bd8d5264292e07568 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 13:59:36 +0200 Subject: [PATCH 055/160] Autobuild nightly fix --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index a6d4aa77..bdc7b0d6 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: 1.2.1 + version: nightly experimental: true - app: app_sdk path: backend/app_sdk From b7e525e0c24f2b30e2aaa1c787b64384deba3b36 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 17:38:25 +0200 Subject: [PATCH 056/160] 1.2.1 rebuild with version change --- .github/workflows/dockerbuild.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index bdc7b0d6..0a3337d9 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: nightly + version: 1.2.1 experimental: true - app: app_sdk path: backend/app_sdk @@ -71,7 +71,7 @@ jobs: cache-to: type=local,dest=/tmp/.buildx-cache tags: | ghcr.io/shuffle/shuffle-${{ matrix.app }}:nightly - ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:nightly + ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:{{ matrix.version }} - name: Image digest run: echo ${{ steps.docker_build.outputs.digest }} From 22169c8b8d33e4ff344b661d761f366143e6fa51 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 17:39:02 +0200 Subject: [PATCH 057/160] nightly override again --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 0a3337d9..9cdbc70c 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: 1.2.1 + version: nightly experimental: true - app: app_sdk path: backend/app_sdk From b8cb914465680ae89e8bef2e68856e2a417446f2 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 17:46:25 +0200 Subject: [PATCH 058/160] 1.2.1 rebuild again with matrix pattern matching --- .github/workflows/dockerbuild.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 9cdbc70c..fd9bec13 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -33,24 +33,24 @@ jobs: experimental: true steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 with: platforms: 'amd64,arm64,arm' - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to Ghcr - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -58,7 +58,7 @@ jobs: - name: Ghcr Build and push id: docker_build - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 env: BUILDX_NO_DEFAULT_LOAD: true with: @@ -70,8 +70,8 @@ jobs: cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache tags: | - ghcr.io/shuffle/shuffle-${{ matrix.app }}:nightly - ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:{{ matrix.version }} + ghcr.io/shuffle/shuffle-${{ matrix.app }}:${{ matrix.version }} + ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:${{ matrix.version }} - name: Image digest run: echo ${{ steps.docker_build.outputs.digest }} From dc0168a7951b20d368d492a5a0b767e7bc74c77e Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 17:53:54 +0200 Subject: [PATCH 059/160] Another 1.2.1 rebuild... --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index fd9bec13..0e8ecfa6 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: nightly + version: 1.2.1 experimental: true - app: app_sdk path: backend/app_sdk From ffb011dd856a239d18ed02a636a1085e7eaf3150 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 28 Sep 2023 17:54:10 +0200 Subject: [PATCH 060/160] Backeeend nightly --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 0e8ecfa6..fd9bec13 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -17,7 +17,7 @@ jobs: experimental: true - app: backend path: backend - version: 1.2.1 + version: nightly experimental: true - app: app_sdk path: backend/app_sdk From 7f2fa65003a03da3d79eff6ee95b33b1d7ef6f48 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 29 Sep 2023 16:55:02 +0200 Subject: [PATCH 061/160] Added 2gb ram requirement to install --- .github/install-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index 48b3689e..88c7299a 100755 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -8,7 +8,7 @@ The Docker setup is done with docker-compose **PS: if you're setting up Shuffle on Windows, go to the next step (Windows Docker setup)** -1. Make sure you have [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed. +1. Make sure you have [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed, and that you have a minimum of **2Gb of RAM** available. 2. Download Shuffle ```bash git clone https://github.com/Shuffle/Shuffle From 7e0a73d007fbea0ee8c3fee827b3e080e3c97c67 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Fri, 29 Sep 2023 18:14:06 +0000 Subject: [PATCH 062/160] more k8s stuff --- backend/Dockerfile | 2 + backend/build-n-push.sh | 21 + backend/go-app/= | 0 backend/go-app/docker.go | 261 ++++-- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 766 ++++++++++++++++++ backend/go-app/main.go | 2 + backend/go-app/naming | 0 backend/go-app/shuffle-shared | 1 + functions/k8s-confs/backend-deployment.yaml | 115 ++- functions/k8s-confs/env-configmap.yaml | 7 +- functions/k8s-confs/kaniko.yaml | 30 +- .../k8s-confs/opensearch-deployment.yaml | 8 +- functions/k8s-confs/orborus-deployment.yaml | 14 +- functions/k8s-confs/shuffle-role.yaml | 4 +- functions/onprem/orborus/Dockerfile | 7 + functions/onprem/orborus/orborus.go | 228 +----- functions/onprem/worker/Dockerfile | 2 +- functions/onprem/worker/worker.go | 49 +- 19 files changed, 1199 insertions(+), 320 deletions(-) create mode 100755 backend/build-n-push.sh create mode 100644 backend/go-app/= create mode 100644 backend/go-app/go.sum create mode 100644 backend/go-app/naming create mode 160000 backend/go-app/shuffle-shared diff --git a/backend/Dockerfile b/backend/Dockerfile index 5bc61f64..1e2d2568 100755 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -8,6 +8,8 @@ ADD ./go-app/main.go /app ADD ./go-app/walkoff.go /app ADD ./go-app/docker.go /app +ADD ./go-app/shuffle-shared /app/shuffle-shared + ADD ./go-app/go.mod /app # Required files for code generation diff --git a/backend/build-n-push.sh b/backend/build-n-push.sh new file mode 100755 index 00000000..05643110 --- /dev/null +++ b/backend/build-n-push.sh @@ -0,0 +1,21 @@ + +docker build . -t shuffle-backend:v1 + +# Check if backend.tar exists and delete it if it does +if [ -f "/root/shuffle-k8s/image-temp/backend.tar" ]; then + echo "backend.tar exists. Deleting..." + rm -rf /root/shuffle-k8s/image-temp/backend.tar +fi + +# Save the Docker image to a tar file +docker save shuffle-backend:v1 -o /root/shuffle-k8s/image-temp/backend.tar + +ctr -n=k8s.io image import /root/shuffle-k8s/image-temp/backend.tar + +#SSHPASS='redhat' +#sshpass -p $SSHPASS scp /root/shuffle-k8s/image-temp/backend.tar root@172.17.14.92:/root/temp-images +# +## Import the image using ctr +#sshpass -p $SSHPASS ssh root@172.17.14.92 "ctr -n=k8s.io image import /root/temp-images/backend.tar" +# +echo "Done!" diff --git a/backend/go-app/= b/backend/go-app/= new file mode 100644 index 00000000..e69de29b diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index a6324faf..3f6d07b3 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -33,6 +33,15 @@ import ( "net/http" "os" "strings" + + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "time" + // "k8s.io/client-go/tools/clientcmd" + // "k8s.io/client-go/util/homedir" ) // Parses a directory with a Dockerfile into a tar for Docker images.. @@ -318,70 +327,208 @@ func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder strin return nil } +func getK8sClient() (*kubernetes.Clientset, error) { + config, err := rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("[ERROR] failed to get in-cluster config: %v", err) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("[ERROR] failed to create Kubernetes client: %v", err) + } + + return clientset, nil +} + +func deleteJob(client *kubernetes.Clientset, jobName, namespace string) error { + deletePolicy := metav1.DeletePropagationForeground + return client.BatchV1().Jobs(namespace).Delete(context.TODO(), jobName, metav1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + }) +} + func buildImage(tags []string, dockerfileFolder string) error { - ctx := context.Background() - client, err := client.NewEnvClient() - if err != nil { - log.Printf("Unable to create docker client: %s", err) - return err + + isKubernetes := false + if os.Getenv("IS_KUBERNETES") == "true" { + isKubernetes = true } - log.Printf("[INFO] Docker Tags: %s", tags) - dockerfileSplit := strings.Split(dockerfileFolder, "/") + if isKubernetes { + log.Printf("K8S ###################") + log.Print("dockerfileFolder: ", dockerfileFolder) + log.Print("tags: ", tags) + log.Print("only tag: ", tags[1]) - // Create a buffer - buf := new(bytes.Buffer) - tw := tar.NewWriter(buf) - defer tw.Close() - baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") - - // Builds the entire folder into buf - err = getParsedTar(tw, baseDir, "") - if err != nil { - log.Printf("Tar issue: %s", err) - } - - dockerFileTarReader := bytes.NewReader(buf.Bytes()) - buildOptions := types.ImageBuildOptions{ - Remove: true, - Tags: tags, - BuildArgs: map[string]*string{}, - } - //NetworkMode: "host", - - httpProxy := os.Getenv("HTTP_PROXY") - if len(httpProxy) > 0 { - buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy - } - httpsProxy := os.Getenv("HTTPS_PROXY") - if len(httpProxy) > 0 { - buildOptions.BuildArgs["https_proxy"] = &httpsProxy - } - - // Build the actual image - imageBuildResponse, err := client.ImageBuild( - ctx, - dockerFileTarReader, - buildOptions, - ) - - if err != nil { - return err - } - - // Read the STDOUT from the build process - defer imageBuildResponse.Body.Close() - buildBuf := new(strings.Builder) - _, err = io.Copy(buildBuf, imageBuildResponse.Body) - if err != nil { - return err - } else { - if strings.Contains(buildBuf.String(), "errorDetail") { - log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) - return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) + registryName := "" + if len(os.Getenv("REGISTRY_URL")) > 0 { + registryName = os.Getenv("REGISTRY_URL") } + + log.Printf("[INFO] registry name: %s", registryName) + + contextDir := strings.Replace(dockerfileFolder, "Dockerfile", "", -1) + contextDir = "/app/" + contextDir + log.Print("contextDir: ", contextDir) + dockerFile := "./Dockerfile" + + client, err := getK8sClient() + if err != nil { + fmt.Printf("Unable to authencticate : %v\n", err) + return err + } + + job := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "shuffle-app-builder", + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "kaniko", + Image: "gcr.io/kaniko-project/executor:latest", + Args: []string{ + "--verbosity=debug", + "--dockerfile=" + dockerFile, + "--context=dir://" + contextDir, + "--skip-tls-verify", + "--destination=" + registryName + "/" + tags[1], + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "kaniko-workspace", + MountPath: "/app/generated", + }, + }, + }, + }, + NodeSelector: map[string]string{ + "node": "master", + }, + RestartPolicy: corev1.RestartPolicyNever, + Volumes: []corev1.Volume{ + { + Name: "kaniko-workspace", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "backend-apps-claim", + }, + }, + }, + }, + }, + }, + }, + } + + createdJob, err := client.BatchV1().Jobs("shuffle").Create(context.TODO(), job, metav1.CreateOptions{}) + if err != nil { + log.Printf("Failed to start image builder job: %s", err) + return err + } + + timeout := time.After(5 * time.Minute) + tick := time.Tick(5 * time.Second) + + for { + select { + case <-timeout: + return fmt.Errorf("job didn't complete within the expected time") + case <-tick: + currentJob, err := client.BatchV1().Jobs("shuffle").Get(context.TODO(), createdJob.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("[ERROR] failed to fetch %s status: %v", createdJob.Name, err) + } + + if currentJob.Status.Succeeded > 0 { + log.Printf("[INFO] Job %s completed successfully!", createdJob.Name) + log.Printf("[INFO] Cleaning up the job %s", createdJob.Name) + err := deleteJob(client, createdJob.Name, "shuffle") + if err != nil { + return fmt.Errorf("[ERROR] failed deleting job %s with error: %s", createdJob.Name, err) + } + log.Println("Job deleted successfully!") + return nil + } else if currentJob.Status.Failed > 0 { + log.Printf("[ERROR] %s job failed with error: %s", createdJob.Name, err) + err := deleteJob(client, createdJob.Name, "shuffle") + if err != nil { + return fmt.Errorf("[ERROR] failed deleting job %s with error: %s", createdJob.Name, err) + } + } + } + } + } else { + + //docker part here } + // ctx := context.Background() + // client, err := client.NewEnvClient() + // if err != nil { + // log.Printf("Unable to create docker client: %s", err) + // return err + // } + + // log.Printf("[INFO] Docker Tags: %s", tags) + // dockerfileSplit := strings.Split(dockerfileFolder, "/") + + // // Create a buffer + // buf := new(bytes.Buffer) + // tw := tar.NewWriter(buf) + // defer tw.Close() + // baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") + + // // Builds the entire folder into buf + // err = getParsedTar(tw, baseDir, "") + // if err != nil { + // log.Printf("Tar issue: %s", err) + // } + + // dockerFileTarReader := bytes.NewReader(buf.Bytes()) + // buildOptions := types.ImageBuildOptions{ + // Remove: true, + // Tags: tags, + // BuildArgs: map[string]*string{}, + // } + // //NetworkMode: "host", + + // httpProxy := os.Getenv("HTTP_PROXY") + // if len(httpProxy) > 0 { + // buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy + // } + // httpsProxy := os.Getenv("HTTPS_PROXY") + // if len(httpProxy) > 0 { + // buildOptions.BuildArgs["https_proxy"] = &httpsProxy + // } + + // // Build the actual image + // imageBuildResponse, err := client.ImageBuild( + // ctx, + // dockerFileTarReader, + // buildOptions, + // ) + + // if err != nil { + // return err + // } + + // // Read the STDOUT from the build process + // defer imageBuildResponse.Body.Close() + // buildBuf := new(strings.Builder) + // _, err = io.Copy(buildBuf, imageBuildResponse.Body) + // if err != nil { + // return err + // } else { + // if strings.Contains(buildBuf.String(), "errorDetail") { + // log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) + // return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) + // } + // } + return nil } diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index b43db79b..26cc6177 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/shuffle/shuffle-shared => /app/shuffle-shared go 1.19 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum new file mode 100644 index 00000000..a24d3992 --- /dev/null +++ b/backend/go-app/go.sum @@ -0,0 +1,766 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0= +cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/iam v1.0.1 h1:lyeCAU6jpnVNrE9zGQkTl3WgNgK/X+uWwaw0kynZJMU= +cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.31.0 h1:aXdyyJz90kA+bor9+6+xHAciMD5mj8v15WqFZ5E0sek= +cloud.google.com/go/pubsub v1.31.0/go.mod h1:dYmJ3K97NCQ/e4OwZ20rD4Ym3Bu8Gu9m/aJdWQjdcks= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= +github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= +github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= +github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= +github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= +github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4= +github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw= +github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw= +github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= +github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82 h1:9bAydALqAjBfPHd/eAiJBHnMZUYov8m2PkXVr+YGQeI= +github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA14J0sA3Hph4dt+AfCjPrYR13+vVodshQSM7km9qw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns= +github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg= +github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ= +github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= +github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0= +github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= +github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw= +github.com/fsouza/go-dockerclient v1.9.7/go.mod h1:vx9C32kE2D15yDSOMCDaAEIARZpDQDFBHeqL3MgQy/U= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= +github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= +github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg= +github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= +github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= +github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= +github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= +github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= +github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo= +github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= +github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= +github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= +go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo= +google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.125.0 h1:7xGvEY4fyWbhWMHf3R2/4w7L4fXyfpRGE9g6lp8+DCk= +google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ee4622d9..aad41804 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3500,6 +3500,7 @@ func handleCloudExecutionOnprem(workflowId, startNode, executionSource, executio } func handleCloudJob(job shuffle.CloudSyncJob) error { + ctx := context.Background() // May need authentication in all of these..? log.Printf("[INFO] Handle job with type %s and action %s", job.Type, job.Action) shuffle.IncrementCache(ctx, job.OrgId, "org_sync_actions") @@ -6097,6 +6098,7 @@ func initHandlers() { // Had to move away from mux, which means Method is fucked up right now. func main() { + initHandlers() hostname, err := os.Hostname() if err != nil { diff --git a/backend/go-app/naming b/backend/go-app/naming new file mode 100644 index 00000000..e69de29b diff --git a/backend/go-app/shuffle-shared b/backend/go-app/shuffle-shared new file mode 160000 index 00000000..355bc9e1 --- /dev/null +++ b/backend/go-app/shuffle-shared @@ -0,0 +1 @@ +Subproject commit 355bc9e1f9a405c12b932d2f0383fdff49063292 diff --git a/functions/k8s-confs/backend-deployment.yaml b/functions/k8s-confs/backend-deployment.yaml index 9dc1b390..c6f1dbfc 100644 --- a/functions/k8s-confs/backend-deployment.yaml +++ b/functions/k8s-confs/backend-deployment.yaml @@ -3,33 +3,58 @@ apiVersion: v1 kind: PersistentVolume metadata: - name: shuffle-pv + name: shuffle-apps-pv spec: capacity: - storage: 10Gi - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: shuffle-storage - hostPath: - path: /mnt/shuffle-data/backend - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: backend-files-claim - name: backend-files-claim -spec: + storage: 10Gi accessModes: - ReadWriteOnce - resources: - requests: - storage: 1Gi -status: {} + persistentVolumeReclaimPolicy: Retain + storageClassName: fast + local: + path: /mnt/shuffle-data/backend + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: node + operator: In + values: + - master + +# --- + +# apiVersion: v1 +# kind: PersistentVolume +# metadata: +# name: shuffle-files-pv +# spec: +# capacity: +# storage: 5Gi +# accessModes: +# - ReadWriteOnce +# persistentVolumeReclaimPolicy: Retain +# storageClassName: fast +# hostPath: +# path: /mnt/shuffle-data/backend + +# --- + +# apiVersion: v1 +# kind: PersistentVolumeClaim +# metadata: +# creationTimestamp: null +# labels: +# io.kompose.service: backend-files-claim +# name: backend-files-claim +# spec: +# accessModes: +# - ReadWriteOnce +# storageClassName: fast +# resources: +# requests: +# storage: 1Gi +# status: {} --- @@ -43,10 +68,11 @@ metadata: spec: accessModes: - ReadWriteOnce + storageClassName: fast resources: requests: storage: 1Gi -status: {} +# status: {} --- apiVersion: apps/v1 @@ -79,15 +105,21 @@ spec: name: shuffle-backend spec: volumes: - - name: shuffle-files - persistentVolumeClaim: - claimName: backend-files-claim + # - name: shuffle-files + # persistentVolumeClaim: + # claimName: backend-files-claim + # - name: shuffle-apps + # hostPath: + # path: /mnt/shuffle-data/backend + # type: DirectoryOrCreate - name: shuffle-apps persistentVolumeClaim: claimName: backend-apps-claim - - name: docker-socket - hostPath: - path: /var/run/docker.sock + # - name: docker-socket + # hostPath: + # path: /var/run/docker.sock + nodeSelector: + node: master containers: - env: - name: BACKEND_HOSTNAME @@ -350,18 +382,29 @@ spec: configMapKeyRef: key: TZ name: env - image: ghcr.io/shuffle/shuffle-backend:latest + - name: IS_KUBERNETES + valueFrom: + configMapKeyRef: + key: IS_KUBERNETES + name: env + - name: REGISTRY_URL + valueFrom: + configMapKeyRef: + key: REGISTRY_URL + name: env + image: shuffle-backend:v1 + imagePullPolicy: Never name: shuffle-backend ports: - containerPort: 5001 resources: {} volumeMounts: - - name: docker-socket - mountPath: /var/run/docker.sock + # - name: docker-socket + # mountPath: /var/run/docker.sock - name: shuffle-apps - mountPath: /shuffle-apps - - name: shuffle-files - mountPath: /shuffle-files + mountPath: /app/generated + # - name: shuffle-files + # mountPath: /shuffle-files restartPolicy: Always status: {} diff --git a/functions/k8s-confs/env-configmap.yaml b/functions/k8s-confs/env-configmap.yaml index b425524f..ad02ad97 100644 --- a/functions/k8s-confs/env-configmap.yaml +++ b/functions/k8s-confs/env-configmap.yaml @@ -2,7 +2,7 @@ apiVersion: v1 data: BACKEND_HOSTNAME: shuffle-backend BACKEND_PORT: "5001" - BASE_URL: http://10.0.244.224:5001 + BASE_URL: http://shuffle-backend:5001 DATASTORE_EMULATOR_HOST: shuffle-database:8000 DB_LOCATION: /mnt/shuffle-data/open-search DOCKER_API_VERSION: "1.40" @@ -44,7 +44,7 @@ data: SHUFFLE_OPENSEARCH_PASSWORD: admin SHUFFLE_OPENSEARCH_PROXY: "" SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: "true" - SHUFFLE_OPENSEARCH_URL: https://192.168.49.2:31001 + SHUFFLE_OPENSEARCH_URL: https://opensearch:9200 SHUFFLE_OPENSEARCH_USERNAME: admin SHUFFLE_ORBORUS_STARTUP_DELAY: "\t\t" SHUFFLE_PASS_APP_PROXY: "FALSE" @@ -52,6 +52,9 @@ data: SHUFFLE_RERUN_SCHEDULE: "300" SSO_REDIRECT_URL: "" TZ: "Europe/Amsterdam \t\t\t\t\t" + IS_KUBERNETES: "true" + REGISTRY_URL: "172.17.14.71:5000" + #REGISTRY_AUTH: "" kind: ConfigMap metadata: creationTimestamp: null diff --git a/functions/k8s-confs/kaniko.yaml b/functions/k8s-confs/kaniko.yaml index 9810da1c..bbeb261e 100644 --- a/functions/k8s-confs/kaniko.yaml +++ b/functions/k8s-confs/kaniko.yaml @@ -1,3 +1,4 @@ + apiVersion: batch/v1 kind: Job metadata: @@ -5,20 +6,29 @@ metadata: spec: template: spec: + nodeSelector: + node: master containers: - name: kaniko - image: gcr.io/kaniko-project/executor:latest - args: ["--verbosity=debug", - "--dockerfile=/workspace/Dockerfile", - "--context=dir:///workspace", - "--insecure", - "--destination=shuffle-registry:5000/shuffle/dhaval-repo:1.0" - ] + image: gcr.io/kaniko-project/executor:debug + # command: ["/busybox/sh", "-c"] + # args: ["-c","while true; do sleep 3600; done"] + args: [ + "--verbosity=debug", + "--context=dir:///app/generated/ok-349ec164d4ad2409152dcb901257f718", + "--dockerfile=./Dockerfile", + #"--no-push", + "--skip-tls-verify", + # "--registry-certificate=172.17.14.71:5000=/workspace/certs/cert.pem", + "--destination=172.17.14.71:5000/shuffle/dhaval-kaniko:1.0" + ] volumeMounts: - name: kaniko-workspace - mountPath: /workspace + mountPath: /app/generated + nodeSelector: + node: master restartPolicy: Never volumes: - name: kaniko-workspace - hostPath: - path: /home/docker/kaniko \ No newline at end of file + persistentVolumeClaim: + claimName: backend-apps-claim diff --git a/functions/k8s-confs/opensearch-deployment.yaml b/functions/k8s-confs/opensearch-deployment.yaml index 37e2e71a..4d0e996a 100644 --- a/functions/k8s-confs/opensearch-deployment.yaml +++ b/functions/k8s-confs/opensearch-deployment.yaml @@ -10,7 +10,7 @@ spec: accessModes: - ReadWriteOnce # This allows read-write access to a single node persistentVolumeReclaimPolicy: Retain # Adjust the reclaim policy as per your needs - storageClassName: shuffle-storage # Set the desired storage class + storageClassName: fast # Set the desired storage class hostPath: path: /mnt/shuffle-data/open-search @@ -26,6 +26,7 @@ metadata: spec: accessModes: - ReadWriteOnce + storageClassName: fast resources: requests: storage: 500Mi @@ -58,6 +59,11 @@ spec: io.kompose.network/shuffle: "true" io.kompose.service: opensearch spec: + # securityContext: + # runAsUser: 1000 # UID + # fsGroup: 1000 # GID + nodeSelector: + node: worker1 containers: - env: - name: OPENSEARCH_JAVA_OPTS diff --git a/functions/k8s-confs/orborus-deployment.yaml b/functions/k8s-confs/orborus-deployment.yaml index 3725aa6d..7686c349 100644 --- a/functions/k8s-confs/orborus-deployment.yaml +++ b/functions/k8s-confs/orborus-deployment.yaml @@ -31,7 +31,7 @@ spec: containers: - env: - name: BASE_URL - value: "http://192.168.49.2:30009" + value: "http://shuffle-backend:5001" - name: DOCKER_API_VERSION value: "1.40" - name: ENVIRONMENT_NAME @@ -46,8 +46,18 @@ spec: #value: run - name: SHUFFLE_WORKER_VERSION value: nightly + - name: IS_KUBERNETES + valueFrom: + configMapKeyRef: + key: IS_KUBERNETES + name: env + - name: REGISTRY_URL + valueFrom: + configMapKeyRef: + key: REGISTRY_URL + name: env - image: orborus:v2 + image: shuffle-orborus:v1 imagePullPolicy: Never name: shuffle-orborus resources: {} diff --git a/functions/k8s-confs/shuffle-role.yaml b/functions/k8s-confs/shuffle-role.yaml index 02aea8c4..bf2afbe9 100644 --- a/functions/k8s-confs/shuffle-role.yaml +++ b/functions/k8s-confs/shuffle-role.yaml @@ -7,4 +7,6 @@ rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "create", "update", "delete"] - +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["create", "get", "list", "watch", "delete"] diff --git a/functions/onprem/orborus/Dockerfile b/functions/onprem/orborus/Dockerfile index 3a8eb059..9eed284a 100755 --- a/functions/onprem/orborus/Dockerfile +++ b/functions/onprem/orborus/Dockerfile @@ -23,4 +23,11 @@ FROM alpine:3.15.0 RUN apk add --no-cache bash tzdata COPY --from=builder /app/ / +ENV ENVIRONMENT_NAME=Shuffle +ENV BASE_URL=http://shuffle-backend:5001 +ENV DOCKER_API_VERSION=1.39 +ENV SHUFFLE_OPENSEARCH_URL=https://opensearch:9200 + + + CMD ["./orborus"] diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index c2ee0f4c..47a79c12 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -44,13 +44,13 @@ import ( //k8s deps "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/homedir" "k8s.io/client-go/rest" - "path/filepath" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + "path/filepath" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Starts jobs in bulk, so this could be increased @@ -243,11 +243,11 @@ func deployServiceWorkers(image string) { // this assumes that the machine should have at least 2 network // interfaces. If not, we will use the default MTU. // interface 1 is the loopback interface - // interface 2 is eth0, The eth0 interface inside a + // interface 2 is eth0, The eth0 interface inside a // Docker container corresponds to the virtual Ethernet // interface that connects the container to the docker0 log.Printf("[ERROR] Failed to get enough network interfaces") - } else { + } else { // Get the preferred interface for _, iface := range interfaces { if strings.Contains(iface.Name, bridgeName) { @@ -257,7 +257,7 @@ func deployServiceWorkers(image string) { break } } - } + } // Create the network options with the specified MTU options := make(map[string]string) @@ -579,205 +579,63 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar { } func deployWorker(image string, identifier string, env []string, executionRequest shuffle.ExecutionRequest) error { - // Binds is the actual "-v" volume. - // Max 20% CPU every second - //CPUQuota: 25000, - //CPUPeriod: 100000, - //CPUShares: 256, + if os.Getenv("IS_KUBERNETES") == "true" { + log.Printf("IS_KUBERNETS", os.Getenv("IS_KUBERNETES")) + log.Printf("REGISTRY_URL", os.Getenv("REGISTRY_URL")) - fmt.Printf("ENV: %+v", env) - image = "shuffle-worker:v1" //hard coded image name to test locally - - envMap := make(map[string]string) - for _, envStr := range env { - parts := strings.SplitN(envStr, "=", 2) - if len(parts) == 2 { - envMap[parts[0]] = parts[1] + if len(os.Getenv("REGISTRY_URL")) > 0 && os.Getenv("REGISTRY_URL") != "" { + env = append(env, fmt.Sprintf("REGISTRY_URL=%s", os.Getenv("REGISTRY_URL"))) + env = append(env, fmt.Sprintf("IS_KUBERNETES=%s", os.Getenv("IS_KUBERNETES"))) } - } - clientset, err := getKubernetesClient() - if err != nil { - fmt.Println("[ERROR]Error getting kubernetes client:", err) - os.Exit(1) - } + image = "shuffle-worker:v1" //hard coded image name to test locally + + envMap := make(map[string]string) + for _, envStr := range env { + parts := strings.SplitN(envStr, "=", 2) + if len(parts) == 2 { + envMap[parts[0]] = parts[1] + } + } + + log.Printf("envMap", envMap) + clientset, err := getKubernetesClient() + if err != nil { + fmt.Println("[ERROR]Error getting kubernetes client:", err) + os.Exit(1) + } pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: identifier, + Name: identifier, + Labels: map[string]string{"app": "shuffle-worker"}, }, Spec: corev1.PodSpec{ + NodeSelector: map[string]string{ + "node": "master", + }, Containers: []corev1.Container{ { Name: identifier, Image: image, - Env: buildEnvVars(envMap), + Env: buildEnvVars(envMap), }, }, }, } - + createdPod, err := clientset.CoreV1().Pods("shuffle").Create(context.Background(), pod, metav1.CreateOptions{}) if err != nil { fmt.Fprintf(os.Stderr, "Error creating pod: %v\n", err) os.Exit(1) } - + fmt.Printf("Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace) - ///////////////////////////////////////////// + } else { - // hostConfig := &container.HostConfig{ - // LogConfig: container.LogConfig{ - // Type: "json-file", - // Config: map[string]string{ - // "max-size": "10m", - // }, - // }, - // Resources: container.Resources{}, - // } - - // if len(os.Getenv("DOCKER_HOST")) == 0 { - // if runtime.GOOS == "windows" { - // hostConfig.Binds = []string{`\\.\pipe\docker_engine:\\.\pipe\docker_engine`} - // } else { - // hostConfig.Binds = []string{"/var/run/docker.sock:/var/run/docker.sock:rw"} - // } - // } - - // hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) - - // if strings.ToLower(cleanupEnv) == "true" { - // hostConfig.AutoRemove = true - // } - - // config := &container.Config{ - // Image: image, - // Env: env, - // } - - // //var swarmConfig = os.Getenv("SHUFFLE_SWARM_CONFIG") - // parsedUuid := uuid.NewV4() - // if swarmConfig == "run" || swarmConfig == "swarm" { - // // FIXME: Should we handle replies properly? - // // In certain cases, a workflow may e.g. be aborted already. If it's aborted, that returns - // // a 401 from the worker, which returns an error here - // go sendWorkerRequest(executionRequest) - // //sendWorkerRequest(executionRequest) - - // //err := sendWorkerRequest(executionRequest) - // //if err != nil { - // // log.Printf("[ERROR] Failed worker request for %s: %s", executionRequest.ExecutionId, err) - - // // if strings.Contains(fmt.Sprintf("%s", err), "connection refused") || strings.Contains(fmt.Sprintf("%s", err), "EOF") { - // // workerImage := fmt.Sprintf("%s/%s/shuffle-worker:%s", baseimageregistry, baseimagename, workerVersion) - // // deployServiceWorkers(workerImage) - - // // time.Sleep(time.Duration(10) * time.Second) - // // err = sendWorkerRequest(executionRequest) - // // } - // //} - - // //if err == nil { - // // // FIXME: Readd this? Removed for rerun reasons - // // // executionIds = append(executionIds, executionRequest.ExecutionId) - // //} - // //}() - - // return nil - // } - - // //log.Printf("[INFO] Identifier: %s", identifier) - // cont, err := dockercli.ContainerCreate( - // context.Background(), - // config, - // hostConfig, - // nil, - // nil, - // identifier, - // ) - - // if err != nil { - // if strings.Contains(fmt.Sprintf("%s", err), "Conflict. The container name ") { - // identifier = fmt.Sprintf("%s-%s", identifier, parsedUuid) - // //log.Printf("[INFO] 2 - Identifier: %s", identifier) - // cont, err = dockercli.ContainerCreate( - // context.Background(), - // config, - // hostConfig, - // nil, - // nil, - // identifier, - // ) - - // if err != nil { - // log.Printf("[ERROR] Container create error(2): %s", err) - // return err - // } - // } else { - // log.Printf("[ERROR] Container create error: %s", err) - // return err - // } - // } - - // containerStartOptions := types.ContainerStartOptions{} - // err = dockercli.ContainerStart(context.Background(), cont.ID, containerStartOptions) - // if err != nil { - // // Trying to recreate and start WITHOUT network if it's possible. No extended checks. Old execution system (<0.9.30) - // if strings.Contains(fmt.Sprintf("%s", err), "cannot join network") || strings.Contains(fmt.Sprintf("%s", err), "No such container") { - // hostConfig.NetworkMode = "" - // //container.NetworkMode(fmt.Sprintf("container:%s", containerId)) - // cont, err = dockercli.ContainerCreate( - // context.Background(), - // config, - // hostConfig, - // nil, - // nil, - // identifier+"-2", - // ) - // if err != nil { - // log.Printf("[ERROR] Failed to CREATE container (2): %s", err) - // } - - // err = dockercli.ContainerStart(context.Background(), cont.ID, containerStartOptions) - // if err != nil { - // log.Printf("[ERROR] Failed to start container (2): %s", err) - // } - // } else { - // log.Printf("[ERROR] Failed initial container start. Quitting as this is NOT a simple network issue. Err: %s", err) - // } - - // if err != nil { - // log.Printf("[ERROR] Failed to start worker container in environment %s: %s", environment, err) - // return err - // } else { - // log.Printf("[INFO] Worker Container %s was created under environment %s for execution %s: docker logs %s", cont.ID, environment, executionRequest.ExecutionId, cont.ID) - // } - - // //stats, err := cli.ContainerInspect(context.Background(), containerName) - // //if err != nil { - // // log.Printf("Failed checking worker %s", containerName) - // // return - // //} - - // //containerStatus := stats.ContainerJSONBase.State.Status - // //if containerStatus != "running" { - // // log.Printf("Status of %s is %s. Should be running. Will reset", containerName, containerStatus) - // // err = stopWorker(containerName) - // // if err != nil { - // // log.Printf("Failed stopping worker %s", execution.ExecutionId) - // // return - // // } - - // // err = deployWorke(cli, workerImage, containerName, env) - // // if err != nil { - // // log.Printf("Failed executing worker %s in state %s", execution.ExecutionId, containerStatus) - // // return - // // } - // //} - // } else { - // log.Printf("[INFO] Worker Container %s was created under environment %s: docker logs %s", cont.ID, environment, cont.ID) - // } + // docker part here + } return nil } @@ -1026,7 +884,7 @@ func getKubernetesClient() (*kubernetes.Clientset, error) { // Initial loop etc func main() { - if isRunningInCluster(){ + if isRunningInCluster() { log.Printf("[INFO] Running inside k8s cluster") } @@ -1116,7 +974,7 @@ func main() { ctx := context.Background() // Run by default from now //commenting for now as its stoppoing minikube - // zombiecheck(ctx, workerTimeout) + // zombiecheck(ctx, workerTimeout) log.Printf("[INFO] Running towards %s (BASE_URL) with environment name %s", baseUrl, environment) diff --git a/functions/onprem/worker/Dockerfile b/functions/onprem/worker/Dockerfile index 554f2357..8d3f6155 100755 --- a/functions/onprem/worker/Dockerfile +++ b/functions/onprem/worker/Dockerfile @@ -32,7 +32,7 @@ FROM alpine:3.15.0 ENV SHUFFLE_BASE_IMAGE_REGISTRY=docker.io ENV SHUFFLE_BASE_IMAGE_NAME=frikky/shuffle ENV SHUFFLE_BASE_IMAGE_TAG_SUFFIX=0.8.70 -ENV SHUFFLE_OPENSEARCH_URL=https://192.168.49.2:31001 +ENV SHUFFLE_OPENSEARCH_URL=https://opensearch:9200 ENV SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY=true RUN apk add --no-cache bash tzdata diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index c4982fae..15b7354f 100755 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -38,13 +38,13 @@ import ( "cloud.google.com/go/storage" //k8s deps - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/homedir" - "k8s.io/client-go/rest" - "path/filepath" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + "path/filepath" // "k8s.io/client-go/util/retry" ) @@ -223,12 +223,13 @@ func getKubernetesClient() (*kubernetes.Clientset, error) { // Deploys the internal worker whenever something happens func deployApp(cli *dockerclient.Client, image string, identifier string, env []string, workflowExecution shuffle.WorkflowExecution, action shuffle.Action) error { - // form basic hostConfig log.Printf("HELLO FROM DEPLOYAPP") - // log.Printf("workflow execution: %+v", workflowExecution) log.Printf("image: %s", image) + + log.Printf("IS_KUBERNETES: %s", os.Getenv("IS_KUBERNETES")) + log.Printf("REGISTRY_NAME: %s", os.Getenv("REGISTRY_NAME")) + namespace := "shuffle" - // ctx := context.Background() envMap := make(map[string]string) for _, envStr := range env { @@ -243,16 +244,16 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] fmt.Println("[ERROR]Error getting kubernetes client:", err) os.Exit(1) } - + log.Printf("[DEBUG] Got kubernetes client") str := strings.ToLower(identifier) - strSplit := strings.Split(str, "_") - value := strSplit[0] + strSplit := strings.Split(str, "_") + value := strSplit[0] value = strings.ReplaceAll(value, "_", "-") //fix naming convention - podUuid := uuid.NewV4().String() - podName := fmt.Sprintf("%s-%s", value, podUuid) + podUuid := uuid.NewV4().String() + podName := fmt.Sprintf("%s-%s", value, podUuid) pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -266,7 +267,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] { Name: value, Image: image, - Env: buildEnvVars(envMap), + Env: buildEnvVars(envMap), }, }, }, @@ -291,22 +292,22 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] // container := corev1.EphemeralContainer{ // EphemeralContainerCommon: corev1.EphemeralContainerCommon{ // Image: image, - // Env: buildEnvVars(envMap), - // ImagePullPolicy: corev1.PullIfNotPresent, + // Env: buildEnvVars(envMap), + // ImagePullPolicy: corev1.PullIfNotPresent, // }, // } // pod.Spec.EphemeralContainers = append(pod.Spec.EphemeralContainers, container) // err = retry.RetryOnConflict(retry.DefaultRetry, func() error { - // _, err := clientset.CoreV1().Pods(namespace).Update(context.Background(), pod, metav1.UpdateOptions{}) - // return err - // }) - // if err != nil { - // log.Fatalf("Failed to update Pod %v", err) - // } + // _, err := clientset.CoreV1().Pods(namespace).Update(context.Background(), pod, metav1.UpdateOptions{}) + // return err + // }) + // if err != nil { + // log.Fatalf("Failed to update Pod %v", err) + // } - // fmt.Printf("Ephemeral container added to Pod \n") + // fmt.Printf("Ephemeral container added to Pod \n") // ephemeralContainer := corev1.EphemeralContainer{ // EphemeralContainerCommon: corev1.EphemeralContainerCommon{ @@ -350,7 +351,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] //CPUShares: 128, //CPUQuota: 10000, //CPUPeriod: 100000, - + // hostConfig := &container.HostConfig{ // LogConfig: container.LogConfig{ // Type: "json-file", From c7fc95831fde06061c4488889ee898e4f558748a Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Fri, 6 Oct 2023 16:38:25 +0000 Subject: [PATCH 063/160] added cleanup for apps and worker for k8s --- functions/k8s-confs/[stage-1 | 0 functions/k8s-confs/env-configmap.yaml | 2 +- functions/k8s-confs/orborus-deployment.yaml | 14 +- functions/k8s-confs/shuffle-registry.yaml | 68 +++++ functions/onprem/orborus/orborus.go | 113 +++++--- functions/onprem/worker/worker.go | 298 +++++++------------- 6 files changed, 255 insertions(+), 240 deletions(-) create mode 100644 functions/k8s-confs/[stage-1 create mode 100644 functions/k8s-confs/shuffle-registry.yaml diff --git a/functions/k8s-confs/[stage-1 b/functions/k8s-confs/[stage-1 new file mode 100644 index 00000000..e69de29b diff --git a/functions/k8s-confs/env-configmap.yaml b/functions/k8s-confs/env-configmap.yaml index ad02ad97..fb8d9d28 100644 --- a/functions/k8s-confs/env-configmap.yaml +++ b/functions/k8s-confs/env-configmap.yaml @@ -53,7 +53,7 @@ data: SSO_REDIRECT_URL: "" TZ: "Europe/Amsterdam \t\t\t\t\t" IS_KUBERNETES: "true" - REGISTRY_URL: "172.17.14.71:5000" + REGISTRY_URL: "shuffle-registry:5000" #REGISTRY_AUTH: "" kind: ConfigMap metadata: diff --git a/functions/k8s-confs/orborus-deployment.yaml b/functions/k8s-confs/orborus-deployment.yaml index 7686c349..cc157289 100644 --- a/functions/k8s-confs/orborus-deployment.yaml +++ b/functions/k8s-confs/orborus-deployment.yaml @@ -24,10 +24,10 @@ spec: io.kompose.network/shuffle: "true" io.kompose.service: orborus spec: - volumes: - - name: docker-socket - hostPath: - path: /var/run/docker.sock + # volumes: + # - name: docker-socket + # hostPath: + # path: /var/run/docker.sock containers: - env: - name: BASE_URL @@ -61,9 +61,9 @@ spec: imagePullPolicy: Never name: shuffle-orborus resources: {} - volumeMounts: - - name: docker-socket - mountPath: /var/run/docker.sock + # volumeMounts: + # - name: docker-socket + # mountPath: /var/run/docker.sock hostname: shuffle-orborus restartPolicy: Always status: {} diff --git a/functions/k8s-confs/shuffle-registry.yaml b/functions/k8s-confs/shuffle-registry.yaml new file mode 100644 index 00000000..c6a80858 --- /dev/null +++ b/functions/k8s-confs/shuffle-registry.yaml @@ -0,0 +1,68 @@ +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: shuffle-registry +spec: + replicas: 1 + selector: + matchLabels: + app: shuffle-registry + template: + metadata: + labels: + app: shuffle-registry + spec: + initContainers: + - name: generate-certs + image: frapsoft/openssl + command: ["openssl"] + args: + - req + - -newkey + - rsa:4096 + - -nodes + - -sha256 + - -x509 + - -days + - "365" + - -keyout + - /certs/domain.key + - -out + - /certs/domain.crt + - -subj + - "/CN=shuffle-registry.default.svc.cluster.local" + volumeMounts: + - name: certs + mountPath: /certs + containers: + - name: registry + image: registry:2 + ports: + - containerPort: 5000 + env: + - name: REGISTRY_HTTP_TLS_CERTIFICATE + value: "/certs/domain.crt" + - name: REGISTRY_HTTP_TLS_KEY + value: "/certs/domain.key" + volumeMounts: + - name: certs + mountPath: "/certs" + volumes: + - name: certs + emptyDir: {} + +--- + +apiVersion: v1 +kind: Service +metadata: + name: shuffle-registry +spec: + selector: + app: shuffle-registry + ports: + - protocol: TCP + port: 5000 + targetPort: 5000 diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 47a79c12..adc66075 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -612,6 +612,7 @@ func deployWorker(image string, identifier string, env []string, executionReques Labels: map[string]string{"app": "shuffle-worker"}, }, Spec: corev1.PodSpec{ + RestartPolicy: "Never", NodeSelector: map[string]string{ "node": "master", }, @@ -1306,58 +1307,86 @@ func main() { // Is this ok to do with Docker? idk :) func getRunningWorkers(ctx context.Context, workerTimeout int) int { //log.Printf("[DEBUG] Getting running workers with API version %s", dockerApiVersion) - containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ - All: true, - }) + counter := 0 + if os.Getenv("IS_KUBERNETES") == "true" { + log.Printf("[INFO] getting running workers in kubernetes") - // Automatically updates the version - if err != nil { - log.Printf("[ERROR] Error getting containers: %s", err) + thresholdTime := time.Now().Add(time.Duration(-workerTimeout) * time.Second) - newVersionSplit := strings.Split(fmt.Sprintf("%s", err), "version is") - if len(newVersionSplit) > 1 { - //dockerApiVersion = strings.TrimSpace(newVersionSplit[1]) - log.Printf("[DEBUG] WANT to change the API version to default to %s?", strings.TrimSpace(newVersionSplit[1])) + clientset, err := getKubernetesClient() + if err != nil { + log.Printf("[ERROR] Failed getting kubernetes client: %s", err) + return 0 } - return maxConcurrency - } + labelSelector := "app=shuffle-worker" + pods, podErr := clientset.CoreV1().Pods("shuffle").List(ctx, metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if podErr != nil { + log.Printf("[ERROR] Failed getting running workers: %s", podErr) + return 0 + } - currenttime := time.Now().Unix() - counter := 0 - for _, container := range containers { - // Skip random containers. Only handle things related to Shuffle. - if !strings.Contains(container.Image, baseimagename) { - shuffleFound := false - for _, item := range container.Labels { - if item == "shuffle" { - shuffleFound = true + for _, pod := range pods.Items { + if pod.Status.Phase == "Running" && pod.CreationTimestamp.Time.After(thresholdTime) { + counter++ + } + } + } else { + + containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ + All: true, + }) + + // Automatically updates the version + if err != nil { + log.Printf("[ERROR] Error getting containers: %s", err) + + newVersionSplit := strings.Split(fmt.Sprintf("%s", err), "version is") + if len(newVersionSplit) > 1 { + //dockerApiVersion = strings.TrimSpace(newVersionSplit[1]) + log.Printf("[DEBUG] WANT to change the API version to default to %s?", strings.TrimSpace(newVersionSplit[1])) + } + + return maxConcurrency + } + + currenttime := time.Now().Unix() + + for _, container := range containers { + // Skip random containers. Only handle things related to Shuffle. + if !strings.Contains(container.Image, baseimagename) { + shuffleFound := false + for _, item := range container.Labels { + if item == "shuffle" { + shuffleFound = true + break + } + } + + // Check image name + if !shuffleFound { + continue + } + //} else { + // log.Printf("NAME: %s", container.Image) + } + + for _, name := range container.Names { + // FIXME - add name_version_uid_uid regex check as well + if !strings.HasPrefix(name, "/worker") { + continue + } + + //log.Printf("Time: %d - %d", currenttime-container.Created, int64(workerTimeout)) + if container.State == "running" && currenttime-container.Created < int64(workerTimeout) { + counter += 1 break } } - - // Check image name - if !shuffleFound { - continue - } - //} else { - // log.Printf("NAME: %s", container.Image) - } - - for _, name := range container.Names { - // FIXME - add name_version_uid_uid regex check as well - if !strings.HasPrefix(name, "/worker") { - continue - } - - //log.Printf("Time: %d - %d", currenttime-container.Created, int64(workerTimeout)) - if container.State == "running" && currenttime-container.Created < int64(workerTimeout) { - counter += 1 - break - } } } - return counter } diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 15b7354f..fe47bfac 100755 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -112,6 +112,18 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason log.Printf("[DEBUG][%s] Shutdown (%s) started with reason %#v. Result amount: %d. ResultsSent: %d, Send result: %#v, Parent: %#v", workflowExecution.ExecutionId, workflowExecution.Status, reason, len(workflowExecution.Results), requestsSent, handleResultSend, workflowExecution.ExecutionParent) //reason := "Error in execution" + // if os.Getenv("IS_KUBERNETES") == "true" { + // log.Printf("[DEBUG][%s] Running in Kubernetes shutting down") + // workerPod := fmt.Sprintf("worker-%s", workflowExecution.ExecutionId) + // namespace := "shuffle" + // clientset, err := getKubernetesClient() + // if err != nil { + // log.Printf("[ERROR] Error getting kubernetes client: %s", err) + // return + // } + + // } else { + sleepDuration := 1 if handleResultSend && requestsSent < 2 { shutdownData, err := json.Marshal(workflowExecution) @@ -181,6 +193,8 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason os.Exit(3) } +// } + func isRunningInCluster() bool { _, existsHost := os.LookupEnv("KUBERNETES_SERVICE_HOST") _, existsPort := os.LookupEnv("KUBERNETES_SERVICE_PORT") @@ -225,214 +239,97 @@ func getKubernetesClient() (*kubernetes.Clientset, error) { func deployApp(cli *dockerclient.Client, image string, identifier string, env []string, workflowExecution shuffle.WorkflowExecution, action shuffle.Action) error { log.Printf("HELLO FROM DEPLOYAPP") log.Printf("image: %s", image) + log.Printf("identifier: %s", identifier) log.Printf("IS_KUBERNETES: %s", os.Getenv("IS_KUBERNETES")) log.Printf("REGISTRY_NAME: %s", os.Getenv("REGISTRY_NAME")) - namespace := "shuffle" + if os.Getenv("IS_KUBERNETES") == "true" { - envMap := make(map[string]string) - for _, envStr := range env { - parts := strings.SplitN(envStr, "=", 2) - if len(parts) == 2 { - envMap[parts[0]] = parts[1] + namespace := "shuffle" + + envMap := make(map[string]string) + for _, envStr := range env { + parts := strings.SplitN(envStr, "=", 2) + if len(parts) == 2 { + envMap[parts[0]] = parts[1] + } } - } - clientset, err := getKubernetesClient() - if err != nil { - fmt.Println("[ERROR]Error getting kubernetes client:", err) - os.Exit(1) - } + clientset, err := getKubernetesClient() + if err != nil { + fmt.Println("[ERROR]Error getting kubernetes client:", err) + os.Exit(1) + } - log.Printf("[DEBUG] Got kubernetes client") - str := strings.ToLower(identifier) - strSplit := strings.Split(str, "_") - value := strSplit[0] - value = strings.ReplaceAll(value, "_", "-") + log.Printf("[DEBUG] Got kubernetes client") + str := strings.ToLower(identifier) + strSplit := strings.Split(str, "_") + value := strSplit[0] + value = strings.ReplaceAll(value, "_", "-") - //fix naming convention - podUuid := uuid.NewV4().String() - podName := fmt.Sprintf("%s-%s", value, podUuid) + //fix naming convention + podUuid := uuid.NewV4().String() + podName := fmt.Sprintf("%s-%s", value, podUuid) - pod := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Labels: map[string]string{ - "app": "shuffle-app", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: value, - Image: image, - Env: buildEnvVars(envMap), + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Labels: map[string]string{ + "app": "shuffle-app", + "executionId": workflowExecution.ExecutionId, + }, + }, + Spec: corev1.PodSpec{ + RestartPolicy: "Never", + Containers: []corev1.Container{ + { + Name: value, + Image: image, + Env: buildEnvVars(envMap), + }, }, }, - }, - } - - createdPod, err := clientset.CoreV1().Pods(namespace).Create(context.Background(), pod, metav1.CreateOptions{}) - if err != nil { - fmt.Fprintf(os.Stderr, "Error creating pod: %v\n", err) - os.Exit(1) - } - - fmt.Printf("Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace) - - // workerPod := fmt.Sprintf("worker-%s", workflowExecution.ExecutionId) - // pod, err := clientset.CoreV1().Pods(namespace).Get(context.TODO(), workerPod, metav1.GetOptions{}) - // if err != nil { - // fmt.Printf("Error getting pod: %v\n", err) - // os.Exit(1) - // } - // log.Printf("[DEBUG] Got pod %s", pod.Name) - - // container := corev1.EphemeralContainer{ - // EphemeralContainerCommon: corev1.EphemeralContainerCommon{ - // Image: image, - // Env: buildEnvVars(envMap), - // ImagePullPolicy: corev1.PullIfNotPresent, - // }, - // } - - // pod.Spec.EphemeralContainers = append(pod.Spec.EphemeralContainers, container) - - // err = retry.RetryOnConflict(retry.DefaultRetry, func() error { - // _, err := clientset.CoreV1().Pods(namespace).Update(context.Background(), pod, metav1.UpdateOptions{}) - // return err - // }) - // if err != nil { - // log.Fatalf("Failed to update Pod %v", err) - // } - - // fmt.Printf("Ephemeral container added to Pod \n") - - // ephemeralContainer := corev1.EphemeralContainer{ - // EphemeralContainerCommon: corev1.EphemeralContainerCommon{ - // Image: image, - // Env: buildEnvVars(envMap), - // }, - // } - - // patchBytes := []byte(fmt.Sprintf(`[ - // { - // "op": "add", - // "path": "/spec/ephemeralContainers", - // "value": %s - // } - // ]`, ephemeralContainer)) - - // _, err = clientset.CoreV1().Pods(namespace).PatchEphemeral(context.TODO(), pod.Name, containerName, patchBytes, metav1.PatchOptions{}) - // if err != nil { - // fmt.Printf("Error adding ephemeral container: %v\n", err) - // os.Exit(1) - // } - - // fmt.Println("Ephemeral container added successfully") - - if action.AppName == "shuffle-subflow" { - // Automatic replacement of URL - for paramIndex, param := range action.Parameters { - if param.Name != "backend_url" { - continue - } - - if strings.Contains(param.Value, "shuffle-backend") { - // Automatic replacement as this is default - action.Parameters[paramIndex].Value = os.Getenv("BASE_URL") - log.Printf("[DEBUG][%s] Replaced backend_url with %s", workflowExecution.ExecutionId, os.Getenv("BASE_URL")) - } } + + createdPod, err := clientset.CoreV1().Pods(namespace).Create(context.Background(), pod, metav1.CreateOptions{}) + if err != nil { + fmt.Fprintf(os.Stderr, "Error creating pod: %v\n", err) + os.Exit(1) + } + fmt.Printf("Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace) + } else { + // docker part } - // Max 10% CPU every second - //CPUShares: 128, - //CPUQuota: 10000, - //CPUPeriod: 100000, + return nil +} - // hostConfig := &container.HostConfig{ - // LogConfig: container.LogConfig{ - // Type: "json-file", - // Config: map[string]string{ - // "max-size": "10m", - // }, - // }, - // Resources: container.Resources{}, - // } +func cleanupExecution(clientset *kubernetes.Clientset, workflowExecution shuffle.WorkflowExecution, namespace string) error { - // hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:worker-%s", workflowExecution.ExecutionId)) + workerName := fmt.Sprintf("worker-%s", workflowExecution.ExecutionId) + labelSelector := fmt.Sprintf("app=shuffle-app,executionId=%s", workflowExecution.ExecutionId) - // Removing because log extraction should happen first - // if cleanupEnv == "true" { - // hostConfig.AutoRemove = true - // } + podList, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + return fmt.Errorf("[ERROR]failed to list apps with label selector %s: %v", labelSelector, err) + } - // FIXME: Add proper foldermounts here - //log.Printf("\n\nPRE FOLDERMOUNT\n\n") - //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} - //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} - // volumeBinds := []string{} - // if len(volumeBinds) > 0 { - // log.Printf("[DEBUG] Setting up binds for container!") - // hostConfig.Binds = volumeBinds - // hostConfig.Mounts = []mount.Mount{} - // for _, bind := range volumeBinds { - // if !strings.Contains(bind, ":") || strings.Contains(bind, "..") || strings.HasPrefix(bind, "~") { - // log.Printf("[WARNING] Bind %s is invalid.", bind) - // continue - // } - - // log.Printf("[DEBUG] Appending bind %s", bind) - // bindSplit := strings.Split(bind, ":") - // sourceFolder := bindSplit[0] - // destinationFolder := bindSplit[0] - // hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ - // Type: mount.TypeBind, - // Source: sourceFolder, - // Target: destinationFolder, - // }) - // } - // } else { - // //log.Printf("[WARNING] Not mounting folders") - // } - - // config := &container.Config{ - // Image: image, - // Env: env, - // } - - // Checking as late as possible, just in case. - // newExecId := fmt.Sprintf("%s_%s", workflowExecution.ExecutionId, action.ID) - // _, err := shuffle.GetCache(ctx, newExecId) - // if err == nil { - // log.Printf("\n\n[DEBUG] Result for %s already found - returning\n\n", newExecId) - // return nil - // } - - // cacheData := []byte("1") - // err = shuffle.SetCache(ctx, newExecId, cacheData, 30) - // if err != nil { - // log.Printf("[WARNING] Failed setting cache for action %s: %s", newExecId, err) - // } else { - // log.Printf("[DEBUG] Adding %s to cache. Name: %s", newExecId, action.Name) - // } - - // if action.ExecutionDelay > 0 { - // log.Printf("[DEBUG] Running app %s in docker with delay of %d", action.Name, action.ExecutionDelay) - // waitTime := time.Duration(action.ExecutionDelay) * time.Second - - // time.AfterFunc(waitTime, func() { - // DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) - // }) - // } else { - // log.Printf("[DEBUG] Running app %s in docker NORMALLY as there is no delay set with identifier %s", action.Name, identifier) - // returnvalue := DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) - // log.Printf("[DEBUG] Normal deploy ret: %s", returnvalue) - // return returnvalue - // } + for _, pod := range podList.Items { + err := clientset.CoreV1().Pods(namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("failed to delete app %s: %v", pod.Name, err) + } + fmt.Printf("App %s in namespace %s deleted.\n", pod.Name, namespace) + } + podErr := clientset.CoreV1().Pods(namespace).Delete(context.TODO(), workerName, metav1.DeleteOptions{}) + if podErr != nil { + return fmt.Errorf("[ERROR] failed to delete the worker %s in namespace %s: %v", workerName, namespace, podErr) + } + fmt.Printf("[DEBUG] %s in namespace %s deleted.\n", workerName, namespace) return nil } @@ -1026,7 +923,17 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) { log.Printf("[INFO][%s] BREAKING BECAUSE RESULTS IS SAME LENGTH AS ACTIONS. SHOULD CHECK ALL RESULTS FOR WHETHER THEY'RE DONE", workflowExecution.ExecutionId) validateFinished(workflowExecution) log.Printf("[DEBUG][%s] Shutting down (17)", workflowExecution.ExecutionId) - shutdown(workflowExecution, "", "", true) + if os.Getenv("IS_KUBERNETES") == "true" { + // log.Printf("workflow execution: %#v", workflowExecution) + clientset, err := getKubernetesClient() + if err != nil { + fmt.Println("[ERROR]Error getting kubernetes client:", err) + os.Exit(1) + } + cleanupExecution(clientset, workflowExecution, "shuffle") + } else { + shutdown(workflowExecution, "", "", true) + } return } } @@ -1243,7 +1150,18 @@ func handleDefaultExecution(client *http.Client, req *http.Request, workflowExec if workflowExecution.Status == "FINISHED" || workflowExecution.Status == "SUCCESS" { log.Printf("[INFO][%s] Workflow execution is finished. Exiting worker.", workflowExecution.ExecutionId) log.Printf("[DEBUG] Shutting down (20)") - shutdown(workflowExecution, "", "", true) + //handle workerssssssssss + if os.Getenv("IS_KUBERNETES") == "true" { + // log.Printf("workflow execution: %#v", workflowExecution) + clientset, err := getKubernetesClient() + if err != nil { + fmt.Println("[ERROR]Error getting kubernetes client:", err) + os.Exit(1) + } + cleanupExecution(clientset, workflowExecution, "shuffle") + } else { + shutdown(workflowExecution, "", "", true) + } } log.Printf("[INFO][%s] Status: %s, Results: %d, actions: %d", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extra) From 9bd18c4ac9908cb8263289511fd0aec6c0400d92 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Mon, 9 Oct 2023 12:34:46 +0530 Subject: [PATCH 064/160] removing junk --- backend/go-app/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index fc2fe92f..ce2a0b42 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -6111,7 +6111,6 @@ func initHandlers() { // Had to move away from mux, which means Method is fucked up right now. func main() { initHandlers() - go shuffle.InitOpsWorkflow() hostname, err := os.Hostname() if err != nil { hostname = "MISSING" From 6fbc916902b7c8e32774dde883b8816ce42b2899 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Mon, 9 Oct 2023 17:57:34 +0000 Subject: [PATCH 065/160] fixed backend code --- backend/Dockerfile | 4 +- backend/go-app/docker.go | 110 +++++++++++++++++++-------------------- backend/go-app/go.mod | 4 +- 3 files changed, 57 insertions(+), 61 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 1e2d2568..cb287e8f 100755 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -8,8 +8,6 @@ ADD ./go-app/main.go /app ADD ./go-app/walkoff.go /app ADD ./go-app/docker.go /app -ADD ./go-app/shuffle-shared /app/shuffle-shared - ADD ./go-app/go.mod /app # Required files for code generation @@ -43,4 +41,4 @@ COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certifica WORKDIR /app EXPOSE 5001 -CMD ["./webapp"] +CMD ["./webapp"] \ No newline at end of file diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 3f6d07b3..6d3c7ddb 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -463,72 +463,70 @@ func buildImage(tags []string, dockerfileFolder string) error { } } else { - //docker part here + ctx := context.Background() + client, err := client.NewEnvClient() + if err != nil { + log.Printf("Unable to create docker client: %s", err) + return err } - // ctx := context.Background() - // client, err := client.NewEnvClient() - // if err != nil { - // log.Printf("Unable to create docker client: %s", err) - // return err - // } + log.Printf("[INFO] Docker Tags: %s", tags) + dockerfileSplit := strings.Split(dockerfileFolder, "/") - // log.Printf("[INFO] Docker Tags: %s", tags) - // dockerfileSplit := strings.Split(dockerfileFolder, "/") + // Create a buffer + buf := new(bytes.Buffer) + tw := tar.NewWriter(buf) + defer tw.Close() + baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") - // // Create a buffer - // buf := new(bytes.Buffer) - // tw := tar.NewWriter(buf) - // defer tw.Close() - // baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") + // Builds the entire folder into buf + err = getParsedTar(tw, baseDir, "") + if err != nil { + log.Printf("Tar issue: %s", err) + } - // // Builds the entire folder into buf - // err = getParsedTar(tw, baseDir, "") - // if err != nil { - // log.Printf("Tar issue: %s", err) - // } + dockerFileTarReader := bytes.NewReader(buf.Bytes()) + buildOptions := types.ImageBuildOptions{ + Remove: true, + Tags: tags, + BuildArgs: map[string]*string{}, + } + //NetworkMode: "host", - // dockerFileTarReader := bytes.NewReader(buf.Bytes()) - // buildOptions := types.ImageBuildOptions{ - // Remove: true, - // Tags: tags, - // BuildArgs: map[string]*string{}, - // } - // //NetworkMode: "host", + httpProxy := os.Getenv("HTTP_PROXY") + if len(httpProxy) > 0 { + buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy + } + httpsProxy := os.Getenv("HTTPS_PROXY") + if len(httpProxy) > 0 { + buildOptions.BuildArgs["https_proxy"] = &httpsProxy + } - // httpProxy := os.Getenv("HTTP_PROXY") - // if len(httpProxy) > 0 { - // buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy - // } - // httpsProxy := os.Getenv("HTTPS_PROXY") - // if len(httpProxy) > 0 { - // buildOptions.BuildArgs["https_proxy"] = &httpsProxy - // } + // Build the actual image + imageBuildResponse, err := client.ImageBuild( + ctx, + dockerFileTarReader, + buildOptions, + ) - // // Build the actual image - // imageBuildResponse, err := client.ImageBuild( - // ctx, - // dockerFileTarReader, - // buildOptions, - // ) + if err != nil { + return err + } - // if err != nil { - // return err - // } - - // // Read the STDOUT from the build process - // defer imageBuildResponse.Body.Close() - // buildBuf := new(strings.Builder) - // _, err = io.Copy(buildBuf, imageBuildResponse.Body) - // if err != nil { - // return err - // } else { - // if strings.Contains(buildBuf.String(), "errorDetail") { - // log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) - // return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) - // } - // } + // Read the STDOUT from the build process + defer imageBuildResponse.Body.Close() + buildBuf := new(strings.Builder) + _, err = io.Copy(buildBuf, imageBuildResponse.Body) + if err != nil { + return err + } else { + if strings.Contains(buildBuf.String(), "errorDetail") { + log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) + return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) + } + } + } return nil } diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 26cc6177..0396e5c7 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -replace github.com/shuffle/shuffle-shared => /app/shuffle-shared +// replace github.com/shuffle/shuffle-shared => /app/shuffle-shared go 1.19 @@ -19,7 +19,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.4.19 + github.com/shuffle/shuffle-shared v0.4.50 golang.org/x/crypto v0.9.0 google.golang.org/api v0.125.0 google.golang.org/appengine v1.6.7 From bc089034a4ef5e9faeb8b33182bbbed4b3c1a4ef Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Mon, 9 Oct 2023 18:29:42 +0000 Subject: [PATCH 066/160] worker changes/fixes --- functions/k8s-confs/env-configmap.yaml | 2 +- functions/onprem/worker/Dockerfile | 2 + functions/onprem/worker/worker.go | 157 +++++++++++++++++++++---- 3 files changed, 140 insertions(+), 21 deletions(-) diff --git a/functions/k8s-confs/env-configmap.yaml b/functions/k8s-confs/env-configmap.yaml index fb8d9d28..ad02ad97 100644 --- a/functions/k8s-confs/env-configmap.yaml +++ b/functions/k8s-confs/env-configmap.yaml @@ -53,7 +53,7 @@ data: SSO_REDIRECT_URL: "" TZ: "Europe/Amsterdam \t\t\t\t\t" IS_KUBERNETES: "true" - REGISTRY_URL: "shuffle-registry:5000" + REGISTRY_URL: "172.17.14.71:5000" #REGISTRY_AUTH: "" kind: ConfigMap metadata: diff --git a/functions/onprem/worker/Dockerfile b/functions/onprem/worker/Dockerfile index 8d3f6155..b1bbbe43 100755 --- a/functions/onprem/worker/Dockerfile +++ b/functions/onprem/worker/Dockerfile @@ -32,6 +32,8 @@ FROM alpine:3.15.0 ENV SHUFFLE_BASE_IMAGE_REGISTRY=docker.io ENV SHUFFLE_BASE_IMAGE_NAME=frikky/shuffle ENV SHUFFLE_BASE_IMAGE_TAG_SUFFIX=0.8.70 + +#for k8s ENV SHUFFLE_OPENSEARCH_URL=https://opensearch:9200 ENV SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY=true diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index fe47bfac..cc7dcbc4 100755 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -22,7 +22,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" //"github.com/docker/docker/api/types/filters" - // "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/mount" dockerclient "github.com/docker/docker/client" //"github.com/go-git/go-billy/v5/memfs" @@ -112,18 +112,6 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason log.Printf("[DEBUG][%s] Shutdown (%s) started with reason %#v. Result amount: %d. ResultsSent: %d, Send result: %#v, Parent: %#v", workflowExecution.ExecutionId, workflowExecution.Status, reason, len(workflowExecution.Results), requestsSent, handleResultSend, workflowExecution.ExecutionParent) //reason := "Error in execution" - // if os.Getenv("IS_KUBERNETES") == "true" { - // log.Printf("[DEBUG][%s] Running in Kubernetes shutting down") - // workerPod := fmt.Sprintf("worker-%s", workflowExecution.ExecutionId) - // namespace := "shuffle" - // clientset, err := getKubernetesClient() - // if err != nil { - // log.Printf("[ERROR] Error getting kubernetes client: %s", err) - // return - // } - - // } else { - sleepDuration := 1 if handleResultSend && requestsSent < 2 { shutdownData, err := json.Marshal(workflowExecution) @@ -237,12 +225,10 @@ func getKubernetesClient() (*kubernetes.Clientset, error) { // Deploys the internal worker whenever something happens func deployApp(cli *dockerclient.Client, image string, identifier string, env []string, workflowExecution shuffle.WorkflowExecution, action shuffle.Action) error { - log.Printf("HELLO FROM DEPLOYAPP") + log.Printf("################################### new call to deployApp ###################################") log.Printf("image: %s", image) log.Printf("identifier: %s", identifier) - - log.Printf("IS_KUBERNETES: %s", os.Getenv("IS_KUBERNETES")) - log.Printf("REGISTRY_NAME: %s", os.Getenv("REGISTRY_NAME")) + // log.Printf("execution: %+v", workflowExecution) if os.Getenv("IS_KUBERNETES") == "true" { @@ -268,6 +254,26 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] value := strSplit[0] value = strings.ReplaceAll(value, "_", "-") + // checking if app is generated or not + appDetails := strings.Split(image, ":")[1] + appDetailsSplit := strings.Split(appDetails, "_") + appName := appDetailsSplit[0] + appVersion := appDetailsSplit[1] + + for _, app := range workflowExecution.Workflow.Actions { + log.Printf("[DEBUG] App: %s, Version: %s", appName, appVersion) + log.Printf("[DEBUG] Checking app %s with version %s", app.AppName, app.AppVersion) + if app.AppName == appName && app.AppVersion == appVersion { + if app.Generated == true { + log.Printf("[DEBUG] Generated app, setting local registry") + image = fmt.Sprintf("%s/%s", registryName, image) + break + } else { + log.Printf("[DEBUG] Not generated app, setting shuffle registry") + } + } + } + //fix naming convention podUuid := uuid.NewV4().String() podName := fmt.Sprintf("%s-%s", value, podUuid) @@ -297,11 +303,112 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] fmt.Fprintf(os.Stderr, "Error creating pod: %v\n", err) os.Exit(1) } - fmt.Printf("Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace) + fmt.Printf("[DEBUG] Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace) } else { - // docker part + // form basic hostConfig + ctx := context.Background() + + if action.AppName == "shuffle-subflow" { + // Automatic replacement of URL + for paramIndex, param := range action.Parameters { + if param.Name != "backend_url" { + continue + } + + if strings.Contains(param.Value, "shuffle-backend") { + // Automatic replacement as this is default + action.Parameters[paramIndex].Value = os.Getenv("BASE_URL") + log.Printf("[DEBUG][%s] Replaced backend_url with %s", workflowExecution.ExecutionId, os.Getenv("BASE_URL")) + } + } } + // Max 10% CPU every second + //CPUShares: 128, + //CPUQuota: 10000, + //CPUPeriod: 100000, + hostConfig := &container.HostConfig{ + LogConfig: container.LogConfig{ + Type: "json-file", + Config: map[string]string{ + "max-size": "10m", + }, + }, + Resources: container.Resources{}, + } + + hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:worker-%s", workflowExecution.ExecutionId)) + + // Removing because log extraction should happen first + if cleanupEnv == "true" { + hostConfig.AutoRemove = true + } + + // FIXME: Add proper foldermounts here + //log.Printf("\n\nPRE FOLDERMOUNT\n\n") + //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} + //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} + volumeBinds := []string{} + if len(volumeBinds) > 0 { + log.Printf("[DEBUG] Setting up binds for container!") + hostConfig.Binds = volumeBinds + hostConfig.Mounts = []mount.Mount{} + for _, bind := range volumeBinds { + if !strings.Contains(bind, ":") || strings.Contains(bind, "..") || strings.HasPrefix(bind, "~") { + log.Printf("[WARNING] Bind %s is invalid.", bind) + continue + } + + log.Printf("[DEBUG] Appending bind %s", bind) + bindSplit := strings.Split(bind, ":") + sourceFolder := bindSplit[0] + destinationFolder := bindSplit[0] + hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ + Type: mount.TypeBind, + Source: sourceFolder, + Target: destinationFolder, + }) + } + } else { + //log.Printf("[WARNING] Not mounting folders") + } + + config := &container.Config{ + Image: image, + Env: env, + } + + // Checking as late as possible, just in case. + newExecId := fmt.Sprintf("%s_%s", workflowExecution.ExecutionId, action.ID) + _, err := shuffle.GetCache(ctx, newExecId) + if err == nil { + log.Printf("\n\n[DEBUG] Result for %s already found - returning\n\n", newExecId) + return nil + } + + cacheData := []byte("1") + err = shuffle.SetCache(ctx, newExecId, cacheData, 30) + if err != nil { + log.Printf("[WARNING] Failed setting cache for action %s: %s", newExecId, err) + } else { + log.Printf("[DEBUG] Adding %s to cache. Name: %s", newExecId, action.Name) + } + + if action.ExecutionDelay > 0 { + log.Printf("[DEBUG] Running app %s in docker with delay of %d", action.Name, action.ExecutionDelay) + waitTime := time.Duration(action.ExecutionDelay) * time.Second + + time.AfterFunc(waitTime, func() { + DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) + }) + } else { + log.Printf("[DEBUG] Running app %s in docker NORMALLY as there is no delay set with identifier %s", action.Name, identifier) + returnvalue := DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) + log.Printf("[DEBUG] Normal deploy ret: %s", returnvalue) + return returnvalue + } + return nil + } return nil } @@ -1168,7 +1275,17 @@ func handleDefaultExecution(client *http.Client, req *http.Request, workflowExec if workflowExecution.Status != "EXECUTING" { log.Printf("[WARNING][%s] Exiting as worker execution has status %s!", workflowExecution.ExecutionId, workflowExecution.Status) log.Printf("[DEBUG] Shutting down (21)") - shutdown(workflowExecution, "", "", true) + if os.Getenv("IS_KUBERNETES") == "true" { + // log.Printf("workflow execution: %#v", workflowExecution) + clientset, err := getKubernetesClient() + if err != nil { + fmt.Println("[ERROR]Error getting kubernetes client:", err) + os.Exit(1) + } + cleanupExecution(clientset, workflowExecution, "shuffle") + } else { + shutdown(workflowExecution, "", "", true) + } } setWorkflowExecution(ctx, workflowExecution, false) From eb85e089453eb030ac769e86fbb7a64e080271a5 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Mon, 9 Oct 2023 18:59:56 +0000 Subject: [PATCH 067/160] orborus changes --- functions/k8s-confs/orborus-deployment.yaml | 7 - functions/onprem/orborus/orborus.go | 165 +++++++++++++++++++- 2 files changed, 161 insertions(+), 11 deletions(-) diff --git a/functions/k8s-confs/orborus-deployment.yaml b/functions/k8s-confs/orborus-deployment.yaml index cc157289..05ebfe08 100644 --- a/functions/k8s-confs/orborus-deployment.yaml +++ b/functions/k8s-confs/orborus-deployment.yaml @@ -24,10 +24,6 @@ spec: io.kompose.network/shuffle: "true" io.kompose.service: orborus spec: - # volumes: - # - name: docker-socket - # hostPath: - # path: /var/run/docker.sock containers: - env: - name: BASE_URL @@ -61,9 +57,6 @@ spec: imagePullPolicy: Never name: shuffle-orborus resources: {} - # volumeMounts: - # - name: docker-socket - # mountPath: /var/run/docker.sock hostname: shuffle-orborus restartPolicy: Always status: {} diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index adc66075..5a4d262d 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -36,7 +36,7 @@ import ( //"github.com/docker/docker/api/types/filters" dockerclient "github.com/docker/docker/client" - // uuid "github.com/satori/go.uuid" + uuid "github.com/satori/go.uuid" //"github.com/mackerelio/go-osstat/disk" "github.com/mackerelio/go-osstat/memory" @@ -613,6 +613,7 @@ func deployWorker(image string, identifier string, env []string, executionReques }, Spec: corev1.PodSpec{ RestartPolicy: "Never", + // once images is pushed, we can remove this NodeSelector: map[string]string{ "node": "master", }, @@ -629,13 +630,170 @@ func deployWorker(image string, identifier string, env []string, executionReques createdPod, err := clientset.CoreV1().Pods("shuffle").Create(context.Background(), pod, metav1.CreateOptions{}) if err != nil { fmt.Fprintf(os.Stderr, "Error creating pod: %v\n", err) - os.Exit(1) } fmt.Printf("Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace) } else { - // docker part here + // Binds is the actual "-v" volume. + // Max 20% CPU every second + + //CPUQuota: 25000, + //CPUPeriod: 100000, + //CPUShares: 256, + hostConfig := &container.HostConfig{ + LogConfig: container.LogConfig{ + Type: "json-file", + Config: map[string]string{ + "max-size": "10m", + }, + }, + Resources: container.Resources{}, + } + + if len(os.Getenv("DOCKER_HOST")) == 0 { + if runtime.GOOS == "windows" { + hostConfig.Binds = []string{`\\.\pipe\docker_engine:\\.\pipe\docker_engine`} + } else { + hostConfig.Binds = []string{"/var/run/docker.sock:/var/run/docker.sock:rw"} + } + } + + hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) + + if strings.ToLower(cleanupEnv) == "true" { + hostConfig.AutoRemove = true + } + + config := &container.Config{ + Image: image, + Env: env, + } + + //var swarmConfig = os.Getenv("SHUFFLE_SWARM_CONFIG") + parsedUuid := uuid.NewV4() + if swarmConfig == "run" || swarmConfig == "swarm" { + // FIXME: Should we handle replies properly? + // In certain cases, a workflow may e.g. be aborted already. If it's aborted, that returns + // a 401 from the worker, which returns an error here + go sendWorkerRequest(executionRequest) + //sendWorkerRequest(executionRequest) + + //err := sendWorkerRequest(executionRequest) + //if err != nil { + // log.Printf("[ERROR] Failed worker request for %s: %s", executionRequest.ExecutionId, err) + + // if strings.Contains(fmt.Sprintf("%s", err), "connection refused") || strings.Contains(fmt.Sprintf("%s", err), "EOF") { + // workerImage := fmt.Sprintf("%s/%s/shuffle-worker:%s", baseimageregistry, baseimagename, workerVersion) + // deployServiceWorkers(workerImage) + + // time.Sleep(time.Duration(10) * time.Second) + // err = sendWorkerRequest(executionRequest) + // } + //} + + //if err == nil { + // // FIXME: Readd this? Removed for rerun reasons + // // executionIds = append(executionIds, executionRequest.ExecutionId) + //} + //}() + + return nil + } + + //log.Printf("[INFO] Identifier: %s", identifier) + cont, err := dockercli.ContainerCreate( + context.Background(), + config, + hostConfig, + nil, + nil, + identifier, + ) + + if err != nil { + if strings.Contains(fmt.Sprintf("%s", err), "Conflict. The container name ") { + identifier = fmt.Sprintf("%s-%s", identifier, parsedUuid) + //log.Printf("[INFO] 2 - Identifier: %s", identifier) + cont, err = dockercli.ContainerCreate( + context.Background(), + config, + hostConfig, + nil, + nil, + identifier, + ) + + if err != nil { + log.Printf("[ERROR] Container create error(2): %s", err) + return err + } + } else { + log.Printf("[ERROR] Container create error: %s", err) + return err + } + } + + containerStartOptions := types.ContainerStartOptions{} + err = dockercli.ContainerStart(context.Background(), cont.ID, containerStartOptions) + if err != nil { + // Trying to recreate and start WITHOUT network if it's possible. No extended checks. Old execution system (<0.9.30) + if strings.Contains(fmt.Sprintf("%s", err), "cannot join network") || strings.Contains(fmt.Sprintf("%s", err), "No such container") { + hostConfig.NetworkMode = "" + //container.NetworkMode(fmt.Sprintf("container:%s", containerId)) + cont, err = dockercli.ContainerCreate( + context.Background(), + config, + hostConfig, + nil, + nil, + identifier+"-2", + ) + if err != nil { + log.Printf("[ERROR] Failed to CREATE container (2): %s", err) + } + + err = dockercli.ContainerStart(context.Background(), cont.ID, containerStartOptions) + if err != nil { + log.Printf("[ERROR] Failed to start container (2): %s", err) + } + } else { + log.Printf("[ERROR] Failed initial container start. Quitting as this is NOT a simple network issue. Err: %s", err) + } + + if err != nil { + log.Printf("[ERROR] Failed to start worker container in environment %s: %s", environment, err) + return err + } else { + log.Printf("[INFO] Worker Container %s was created under environment %s for execution %s: docker logs %s", cont.ID, environment, executionRequest.ExecutionId, cont.ID) + } + + //stats, err := cli.ContainerInspect(context.Background(), containerName) + //if err != nil { + // log.Printf("Failed checking worker %s", containerName) + // return + //} + + //containerStatus := stats.ContainerJSONBase.State.Status + //if containerStatus != "running" { + // log.Printf("Status of %s is %s. Should be running. Will reset", containerName, containerStatus) + // err = stopWorker(containerName) + // if err != nil { + // log.Printf("Failed stopping worker %s", execution.ExecutionId) + // return + // } + + // err = deployWorke(cli, workerImage, containerName, env) + // if err != nil { + // log.Printf("Failed executing worker %s in state %s", execution.ExecutionId, containerStatus) + // return + // } + //} + } else { + log.Printf("[INFO] Worker Container %s was created under environment %s: docker logs %s", cont.ID, environment, cont.ID) + } + + return nil } return nil @@ -889,7 +1047,6 @@ func main() { log.Printf("[INFO] Running inside k8s cluster") } - ///////////////////////// startupDelay := os.Getenv("SHUFFLE_ORBORUS_STARTUP_DELAY") if len(startupDelay) > 0 { From b0a3eaf228727d3ffef7c8382c292ca828c15ff8 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Mon, 9 Oct 2023 19:03:49 +0000 Subject: [PATCH 068/160] removing unused files --- backend/build-n-push.sh | 21 --------------------- backend/go-app/= | 0 backend/go-app/shuffle-shared | 1 - 3 files changed, 22 deletions(-) delete mode 100755 backend/build-n-push.sh delete mode 100644 backend/go-app/= delete mode 160000 backend/go-app/shuffle-shared diff --git a/backend/build-n-push.sh b/backend/build-n-push.sh deleted file mode 100755 index 05643110..00000000 --- a/backend/build-n-push.sh +++ /dev/null @@ -1,21 +0,0 @@ - -docker build . -t shuffle-backend:v1 - -# Check if backend.tar exists and delete it if it does -if [ -f "/root/shuffle-k8s/image-temp/backend.tar" ]; then - echo "backend.tar exists. Deleting..." - rm -rf /root/shuffle-k8s/image-temp/backend.tar -fi - -# Save the Docker image to a tar file -docker save shuffle-backend:v1 -o /root/shuffle-k8s/image-temp/backend.tar - -ctr -n=k8s.io image import /root/shuffle-k8s/image-temp/backend.tar - -#SSHPASS='redhat' -#sshpass -p $SSHPASS scp /root/shuffle-k8s/image-temp/backend.tar root@172.17.14.92:/root/temp-images -# -## Import the image using ctr -#sshpass -p $SSHPASS ssh root@172.17.14.92 "ctr -n=k8s.io image import /root/temp-images/backend.tar" -# -echo "Done!" diff --git a/backend/go-app/= b/backend/go-app/= deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/go-app/shuffle-shared b/backend/go-app/shuffle-shared deleted file mode 160000 index 355bc9e1..00000000 --- a/backend/go-app/shuffle-shared +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 355bc9e1f9a405c12b932d2f0383fdff49063292 From 772b1d3693bd70d05b997821401d6753d1ff3bf8 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Mon, 9 Oct 2023 21:00:14 +0000 Subject: [PATCH 069/160] adding new specs --- .../final-specs/shuffle-backend.yaml | 434 ++++++++++++++++++ .../final-specs/final-specs/shuffle-cm.yaml | 63 +++ .../final-specs/shuffle-frontend.yaml | 68 +++ .../final-specs/shuffle-opensearch.yaml | 129 ++++++ .../final-specs/shuffle-orborus.yaml | 62 +++ .../final-specs/final-specs/shuffle-role.yaml | 12 + .../final-specs/shuffle-rolebinding.yaml | 14 + functions/onprem/orborus/orborus.go | 7 +- 8 files changed, 786 insertions(+), 3 deletions(-) create mode 100644 functions/final-specs/final-specs/shuffle-backend.yaml create mode 100644 functions/final-specs/final-specs/shuffle-cm.yaml create mode 100644 functions/final-specs/final-specs/shuffle-frontend.yaml create mode 100644 functions/final-specs/final-specs/shuffle-opensearch.yaml create mode 100644 functions/final-specs/final-specs/shuffle-orborus.yaml create mode 100644 functions/final-specs/final-specs/shuffle-role.yaml create mode 100644 functions/final-specs/final-specs/shuffle-rolebinding.yaml diff --git a/functions/final-specs/final-specs/shuffle-backend.yaml b/functions/final-specs/final-specs/shuffle-backend.yaml new file mode 100644 index 00000000..810499a0 --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-backend.yaml @@ -0,0 +1,434 @@ +#--- +# +#apiVersion: v1 +#kind: PersistentVolume +#metadata: +# name: shuffle-apps-pv +#spec: +# capacity: +# storage: 10Gi +# accessModes: +# - ReadWriteOnce +# persistentVolumeReclaimPolicy: Retain +# storageClassName: fast +# local: +# path: /mnt/shuffle-data/backend +# nodeAffinity: +# required: +# nodeSelectorTerms: +# - matchExpressions: +# - key: node +# operator: In +# values: +# - master +# +## --- + +# apiVersion: v1 +# kind: PersistentVolume +# metadata: +# name: shuffle-files-pv +# spec: +# capacity: +# storage: 5Gi +# accessModes: +# - ReadWriteOnce +# persistentVolumeReclaimPolicy: Retain +# storageClassName: fast +# hostPath: +# path: /mnt/shuffle-data/backend + +# --- + +# apiVersion: v1 +# kind: PersistentVolumeClaim +# metadata: +# creationTimestamp: null +# labels: +# io.kompose.service: backend-files-claim +# name: backend-files-claim +# spec: +# accessModes: +# - ReadWriteOnce +# storageClassName: fast +# resources: +# requests: +# storage: 1Gi +# status: {} + +--- + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: backend-apps-claim + name: backend-apps-claim +spec: + accessModes: + - ReadWriteOnce + storageClassName: shuffle-data + resources: + requests: + storage: 1Gi +# status: {} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: backend + name: backend +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: backend + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: backend + app: shuffle-backend + name: shuffle-backend + spec: + volumes: + # - name: shuffle-files + # persistentVolumeClaim: + # claimName: backend-files-claim + # - name: shuffle-apps + # hostPath: + # path: /mnt/shuffle-data/backend + # type: DirectoryOrCreate + - name: shuffle-apps + persistentVolumeClaim: + claimName: backend-apps-claim + # - name: docker-socket + # hostPath: + # path: /var/run/docker.sock +# nodeSelector: +# node: master + containers: + - env: + - name: BACKEND_HOSTNAME + valueFrom: + configMapKeyRef: + key: BACKEND_HOSTNAME + name: env + - name: BACKEND_PORT + valueFrom: + configMapKeyRef: + key: BACKEND_PORT + name: env + - name: BASE_URL + valueFrom: + configMapKeyRef: + key: BASE_URL + name: env + - name: DATASTORE_EMULATOR_HOST + valueFrom: + configMapKeyRef: + key: DATASTORE_EMULATOR_HOST + name: env + - name: DB_LOCATION + valueFrom: + configMapKeyRef: + key: DB_LOCATION + name: env + - name: DOCKER_API_VERSION + valueFrom: + configMapKeyRef: + key: DOCKER_API_VERSION + name: env + - name: ENVIRONMENT_NAME + valueFrom: + configMapKeyRef: + key: ENVIRONMENT_NAME + name: env + - name: FRONTEND_PORT + valueFrom: + configMapKeyRef: + key: FRONTEND_PORT + name: env + - name: FRONTEND_PORT_HTTPS + valueFrom: + configMapKeyRef: + key: FRONTEND_PORT_HTTPS + name: env + - name: HTTPS_PROXY + valueFrom: + configMapKeyRef: + key: HTTPS_PROXY + name: env + - name: HTTP_PROXY + valueFrom: + configMapKeyRef: + key: HTTP_PROXY + name: env + - name: ORBORUS_CONTAINER_NAME + valueFrom: + configMapKeyRef: + key: ORBORUS_CONTAINER_NAME + name: env + - name: ORG_ID + valueFrom: + configMapKeyRef: + key: ORG_ID + name: env + - name: OUTER_HOSTNAME + valueFrom: + configMapKeyRef: + key: OUTER_HOSTNAME + name: env + - name: SHUFFLE_APP_DOWNLOAD_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_DOWNLOAD_LOCATION + name: env + - name: SHUFFLE_APP_FORCE_UPDATE + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_FORCE_UPDATE + name: env + - name: SHUFFLE_APP_HOTLOAD_FOLDER + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_HOTLOAD_FOLDER + name: env + - name: SHUFFLE_APP_HOTLOAD_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_HOTLOAD_LOCATION + name: env + - name: SHUFFLE_BASE_IMAGE_NAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_BASE_IMAGE_NAME + name: env + - name: SHUFFLE_BASE_IMAGE_REGISTRY + valueFrom: + configMapKeyRef: + key: SHUFFLE_BASE_IMAGE_REGISTRY + name: env + - name: SHUFFLE_BASE_IMAGE_TAG_SUFFIX + valueFrom: + configMapKeyRef: + key: SHUFFLE_BASE_IMAGE_TAG_SUFFIX + name: env + - name: SHUFFLE_CHAT_DISABLED + valueFrom: + configMapKeyRef: + key: SHUFFLE_CHAT_DISABLED + name: env + - name: SHUFFLE_CONTAINER_AUTO_CLEANUP + valueFrom: + configMapKeyRef: + key: SHUFFLE_CONTAINER_AUTO_CLEANUP + name: env + - name: SHUFFLE_DEFAULT_APIKEY + valueFrom: + configMapKeyRef: + key: SHUFFLE_DEFAULT_APIKEY + name: env + - name: SHUFFLE_DEFAULT_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_DEFAULT_PASSWORD + name: env + - name: SHUFFLE_DEFAULT_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_DEFAULT_USERNAME + name: env + - name: SHUFFLE_DOWNLOAD_AUTH_BRANCH + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_AUTH_BRANCH + name: env + - name: SHUFFLE_DOWNLOAD_AUTH_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_AUTH_PASSWORD + name: env + - name: SHUFFLE_DOWNLOAD_AUTH_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_AUTH_USERNAME + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME + name: env + - name: SHUFFLE_ELASTIC + valueFrom: + configMapKeyRef: + key: SHUFFLE_ELASTIC + name: env + - name: SHUFFLE_ENCRYPTION_MODIFIER + valueFrom: + configMapKeyRef: + key: SHUFFLE_ENCRYPTION_MODIFIER + name: env + - name: SHUFFLE_FILE_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_FILE_LOCATION + name: env + - name: SHUFFLE_LOGS_DISABLED + valueFrom: + configMapKeyRef: + key: SHUFFLE_LOGS_DISABLED + name: env + - name: SHUFFLE_OPENSEARCH_APIKEY + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_APIKEY + name: env + - name: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE + name: env + - name: SHUFFLE_OPENSEARCH_CLOUDID + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_CLOUDID + name: env + - name: SHUFFLE_OPENSEARCH_INDEX_PREFIX + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_INDEX_PREFIX + name: env + - name: SHUFFLE_OPENSEARCH_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_PASSWORD + name: env + - name: SHUFFLE_OPENSEARCH_PROXY + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_PROXY + name: env + - name: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY + name: env + - name: SHUFFLE_OPENSEARCH_URL + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_URL + name: env + - name: SHUFFLE_OPENSEARCH_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_USERNAME + name: env + - name: SHUFFLE_ORBORUS_STARTUP_DELAY + valueFrom: + configMapKeyRef: + key: SHUFFLE_ORBORUS_STARTUP_DELAY + name: env + - name: SHUFFLE_PASS_APP_PROXY + valueFrom: + configMapKeyRef: + key: SHUFFLE_PASS_APP_PROXY + name: env + - name: SHUFFLE_PASS_WORKER_PROXY + valueFrom: + configMapKeyRef: + key: SHUFFLE_PASS_WORKER_PROXY + name: env + - name: SHUFFLE_RERUN_SCHEDULE + valueFrom: + configMapKeyRef: + key: SHUFFLE_RERUN_SCHEDULE + name: env + - name: SSO_REDIRECT_URL + valueFrom: + configMapKeyRef: + key: SSO_REDIRECT_URL + name: env + - name: TZ + valueFrom: + configMapKeyRef: + key: TZ + name: env + - name: IS_KUBERNETES + valueFrom: + configMapKeyRef: + key: IS_KUBERNETES + name: env + - name: REGISTRY_URL + valueFrom: + configMapKeyRef: + key: REGISTRY_URL + name: env + image: dhavald055/shuffle-k8s:shuffle-backend-kubernetes + #imagePullPolicy: Never + name: shuffle-backend + ports: + - containerPort: 5001 + resources: {} + volumeMounts: + # - name: docker-socket + # mountPath: /var/run/docker.sock + - name: shuffle-apps + mountPath: /app/generated + # - name: shuffle-files + # mountPath: /shuffle-files + restartPolicy: Always +status: {} + +--- + +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: backend + name: shuffle-backend +spec: + type: NodePort + ports: + - name: "50001" + port: 5001 + targetPort: 5001 + nodePort: 30009 + selector: + io.kompose.service: backend +status: + loadBalancer: {} + diff --git a/functions/final-specs/final-specs/shuffle-cm.yaml b/functions/final-specs/final-specs/shuffle-cm.yaml new file mode 100644 index 00000000..ad02ad97 --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-cm.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +data: + BACKEND_HOSTNAME: shuffle-backend + BACKEND_PORT: "5001" + BASE_URL: http://shuffle-backend:5001 + DATASTORE_EMULATOR_HOST: shuffle-database:8000 + DB_LOCATION: /mnt/shuffle-data/open-search + DOCKER_API_VERSION: "1.40" + ENVIRONMENT_NAME: Shuffle + FRONTEND_PORT: "3001" + FRONTEND_PORT_HTTPS: "3443" + HTTP_PROXY: "" + HTTPS_PROXY: "" + ORBORUS_CONTAINER_NAME: "\t\t\t\t" + ORG_ID: Shuffle + OUTER_HOSTNAME: shuffle-backend + SHUFFLE_APP_DOWNLOAD_LOCATION: https://github.com/shuffle/python-apps + SHUFFLE_APP_FORCE_UPDATE: "false" + SHUFFLE_APP_HOTLOAD_FOLDER: /shuffle-apps + SHUFFLE_APP_HOTLOAD_LOCATION: ./shuffle-apps + SHUFFLE_BASE_IMAGE_NAME: shuffle + SHUFFLE_BASE_IMAGE_REGISTRY: ghcr.io + SHUFFLE_BASE_IMAGE_TAG_SUFFIX: -1.0.0 + SHUFFLE_CHAT_DISABLED: "false" + SHUFFLE_CONTAINER_AUTO_CLEANUP: "false" + SHUFFLE_DEFAULT_APIKEY: "" + SHUFFLE_DEFAULT_PASSWORD: "" + SHUFFLE_DEFAULT_USERNAME: "" + SHUFFLE_DOWNLOAD_AUTH_BRANCH: "" + SHUFFLE_DOWNLOAD_AUTH_PASSWORD: "" + SHUFFLE_DOWNLOAD_AUTH_USERNAME: "" + SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH: "" + SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION: "" + SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD: "" + SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME: "" + SHUFFLE_ELASTIC: "true" + SHUFFLE_ENCRYPTION_MODIFIER: "" + SHUFFLE_FILE_LOCATION: /shuffle-files + SHUFFLE_LOGS_DISABLED: "false" + SHUFFLE_OPENSEARCH_APIKEY: "" + SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: "" + SHUFFLE_OPENSEARCH_CLOUDID: "" + SHUFFLE_OPENSEARCH_INDEX_PREFIX: "" + SHUFFLE_OPENSEARCH_PASSWORD: admin + SHUFFLE_OPENSEARCH_PROXY: "" + SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: "true" + SHUFFLE_OPENSEARCH_URL: https://opensearch:9200 + SHUFFLE_OPENSEARCH_USERNAME: admin + SHUFFLE_ORBORUS_STARTUP_DELAY: "\t\t" + SHUFFLE_PASS_APP_PROXY: "FALSE" + SHUFFLE_PASS_WORKER_PROXY: "TRUE" + SHUFFLE_RERUN_SCHEDULE: "300" + SSO_REDIRECT_URL: "" + TZ: "Europe/Amsterdam \t\t\t\t\t" + IS_KUBERNETES: "true" + REGISTRY_URL: "172.17.14.71:5000" + #REGISTRY_AUTH: "" +kind: ConfigMap +metadata: + creationTimestamp: null + labels: + io.kompose.service: backend-env + name: env diff --git a/functions/final-specs/final-specs/shuffle-frontend.yaml b/functions/final-specs/final-specs/shuffle-frontend.yaml new file mode 100644 index 00000000..cb11f728 --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-frontend.yaml @@ -0,0 +1,68 @@ +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: frontend + name: frontend +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: frontend + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: frontend + spec: + containers: + - env: + - name: BACKEND_HOSTNAME + image: ghcr.io/shuffle/shuffle-frontend:latest + name: shuffle-frontend + ports: + - containerPort: 80 + - containerPort: 443 + resources: {} + hostname: shuffle-frontend + restartPolicy: Always +status: {} + +--- + +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: frontend + name: frontend +spec: + type: NodePort + ports: + - name: "80" + port: 80 + targetPort: 80 + nodePort: 30007 + - name: "443" + port: 443 + targetPort: 443 + nodePort: 30008 + selector: + io.kompose.service: frontend +# status: +# loadBalancer: {} diff --git a/functions/final-specs/final-specs/shuffle-opensearch.yaml b/functions/final-specs/final-specs/shuffle-opensearch.yaml new file mode 100644 index 00000000..6b3631af --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-opensearch.yaml @@ -0,0 +1,129 @@ +#--- +# +#apiVersion: v1 +#kind: PersistentVolume +#metadata: +# name: shuffle-os-pv +#spec: +# capacity: +# storage: 10Gi # Adjust the storage size as per your requirements +# accessModes: +# - ReadWriteOnce # This allows read-write access to a single node +# persistentVolumeReclaimPolicy: Retain # Adjust the reclaim policy as per your needs +# storageClassName: shuffle-data # Set the desired storage class +# hostPath: +# path: /mnt/shuffle-data/open-search +# +--- + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: opensearch-claim0 + name: opensearch-claim0 +spec: + accessModes: + - ReadWriteOnce + storageClassName: shuffle-data + resources: + requests: + storage: 500Mi +status: {} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: opensearch + name: opensearch +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: opensearch + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: opensearch + spec: + # securityContext: + # runAsUser: 1000 # UID + # fsGroup: 1000 # GID + nodeSelector: + node: worker1 + containers: + - env: + - name: OPENSEARCH_JAVA_OPTS + value: -Xms1024m -Xmx1024m + #- name: bootstrap.memory_lock + #value: "true" + - name: cluster.initial_master_nodes + value: shuffle-opensearch + - name: cluster.name + value: shuffle-cluster + - name: cluster.routing.allocation.disk.threshold_enabled + value: "false" + - name: discovery.seed_hosts + value: shuffle-opensearch + - name: node.name + value: shuffle-opensearch + - name: node.store.allow_mmap + value: "false" + - name: DB_LOCATION + valueFrom: + configMapKeyRef: + name: env + key: DB_LOCATION + image: opensearchproject/opensearch:2.5.0 + name: shuffle-opensearch + ports: + - containerPort: 9200 + resources: {} + volumeMounts: + - mountPath: /usr/share/opensearch/data + name: opensearch-claim0 + hostname: shuffle-opensearch + restartPolicy: Always + volumes: + - name: opensearch-claim0 + persistentVolumeClaim: + claimName: opensearch-claim0 +status: {} + +--- + +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: opensearch + name: opensearch +spec: + type: NodePort + ports: + - name: "9200" + port: 9200 + targetPort: 9200 + nodePort: 31001 + selector: + io.kompose.service: opensearch +status: + loadBalancer: {} + diff --git a/functions/final-specs/final-specs/shuffle-orborus.yaml b/functions/final-specs/final-specs/shuffle-orborus.yaml new file mode 100644 index 00000000..d3b9b713 --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-orborus.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: orborus + name: orborus +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: orborus + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: orborus + spec: + containers: + - env: + - name: BASE_URL + value: "http://shuffle-backend:5001" + - name: DOCKER_API_VERSION + value: "1.40" + - name: ENVIRONMENT_NAME + value: Shuffle + - name: ORG_ID + value: Shuffle + - name: SHUFFLE_APP_SDK_VERSION + value: nightly + - name: SHUFFLE_SCALE_REPLICAS + value: "5" + #- name: SHUFFLE_SWARM_CONFIG + #value: run + - name: SHUFFLE_WORKER_VERSION + value: nightly + - name: IS_KUBERNETES + valueFrom: + configMapKeyRef: + key: IS_KUBERNETES + name: env + - name: REGISTRY_URL + valueFrom: + configMapKeyRef: + key: REGISTRY_URL + name: env + + image: dhavald055/shuffle-k8s:shuffle-orborus-kubernetes + #imagePullPolicy: Never + name: shuffle-orborus + resources: {} + hostname: shuffle-orborus + restartPolicy: Always +status: {} diff --git a/functions/final-specs/final-specs/shuffle-role.yaml b/functions/final-specs/final-specs/shuffle-role.yaml new file mode 100644 index 00000000..bf2afbe9 --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-role.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: shuffle + name: pod-manager +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "create", "update", "delete"] +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["create", "get", "list", "watch", "delete"] diff --git a/functions/final-specs/final-specs/shuffle-rolebinding.yaml b/functions/final-specs/final-specs/shuffle-rolebinding.yaml new file mode 100644 index 00000000..fd2225f6 --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-rolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pod-manager-binding + namespace: shuffle +subjects: +- kind: ServiceAccount + name: default + namespace: shuffle +roleRef: + kind: Role + name: pod-manager + apiGroup: rbac.authorization.k8s.io + diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 5a4d262d..a480b9f4 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -614,9 +614,10 @@ func deployWorker(image string, identifier string, env []string, executionReques Spec: corev1.PodSpec{ RestartPolicy: "Never", // once images is pushed, we can remove this - NodeSelector: map[string]string{ - "node": "master", - }, + // keep this when running locally + // NodeSelector: map[string]string{ + // "node": "master", + // }, Containers: []corev1.Container{ { Name: identifier, From 93a53c721318470a1cc3f9601deab059e2a72f7c Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Mon, 9 Oct 2023 21:06:05 +0000 Subject: [PATCH 070/160] added sc --- functions/final-specs/final-specs/shuffle-sc.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 functions/final-specs/final-specs/shuffle-sc.yaml diff --git a/functions/final-specs/final-specs/shuffle-sc.yaml b/functions/final-specs/final-specs/shuffle-sc.yaml new file mode 100644 index 00000000..a8cb64c5 --- /dev/null +++ b/functions/final-specs/final-specs/shuffle-sc.yaml @@ -0,0 +1,7 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: shuffle-data +provisioner: kubernetes.io/no-provisioner +volumeBindingMode: WaitForFirstConsumer + From 7d927d56f485483ac48703d3101d10a8c30e3c86 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Mon, 9 Oct 2023 22:30:05 +0530 Subject: [PATCH 071/160] Update shuffle-opensearch.yaml --- .../final-specs/shuffle-opensearch.yaml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/functions/final-specs/final-specs/shuffle-opensearch.yaml b/functions/final-specs/final-specs/shuffle-opensearch.yaml index 6b3631af..8173064f 100644 --- a/functions/final-specs/final-specs/shuffle-opensearch.yaml +++ b/functions/final-specs/final-specs/shuffle-opensearch.yaml @@ -1,19 +1,19 @@ -#--- -# -#apiVersion: v1 -#kind: PersistentVolume -#metadata: -# name: shuffle-os-pv -#spec: -# capacity: -# storage: 10Gi # Adjust the storage size as per your requirements -# accessModes: -# - ReadWriteOnce # This allows read-write access to a single node -# persistentVolumeReclaimPolicy: Retain # Adjust the reclaim policy as per your needs -# storageClassName: shuffle-data # Set the desired storage class -# hostPath: -# path: /mnt/shuffle-data/open-search -# +--- + +apiVersion: v1 +kind: PersistentVolume +metadata: + name: shuffle-os-pv +spec: + capacity: + storage: 10Gi # Adjust the storage size as per your requirements + accessModes: + - ReadWriteOnce # This allows read-write access to a single node + persistentVolumeReclaimPolicy: Retain # Adjust the reclaim policy as per your needs + storageClassName: shuffle-data # Set the desired storage class + hostPath: + path: /mnt/shuffle-data/open-search + --- apiVersion: v1 From 7a71b1e31d6acdec056807dd238eeb0e9a7159c3 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Mon, 9 Oct 2023 22:32:17 +0530 Subject: [PATCH 072/160] Update shuffle-backend.yaml --- .../final-specs/shuffle-backend.yaml | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/functions/final-specs/final-specs/shuffle-backend.yaml b/functions/final-specs/final-specs/shuffle-backend.yaml index 810499a0..4da4c4cc 100644 --- a/functions/final-specs/final-specs/shuffle-backend.yaml +++ b/functions/final-specs/final-specs/shuffle-backend.yaml @@ -1,27 +1,19 @@ -#--- -# -#apiVersion: v1 -#kind: PersistentVolume -#metadata: -# name: shuffle-apps-pv -#spec: -# capacity: -# storage: 10Gi -# accessModes: -# - ReadWriteOnce -# persistentVolumeReclaimPolicy: Retain -# storageClassName: fast -# local: -# path: /mnt/shuffle-data/backend -# nodeAffinity: -# required: -# nodeSelectorTerms: -# - matchExpressions: -# - key: node -# operator: In -# values: -# - master -# +--- + +apiVersion: v1 +kind: PersistentVolume +metadata: + name: shuffle-apps-pv +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: fast + hostPath: + path: /mnt/shuffle-data/backend + ## --- # apiVersion: v1 From a3967b76317e2743aad2f95f86ea8ad15a1ecb26 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Tue, 10 Oct 2023 11:34:01 +0000 Subject: [PATCH 073/160] getting worker image from registry --- functions/final-specs/final-specs/shuffle-cm.yaml | 1 + functions/final-specs/final-specs/shuffle-orborus.yaml | 5 +++++ functions/onprem/orborus/orborus.go | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/functions/final-specs/final-specs/shuffle-cm.yaml b/functions/final-specs/final-specs/shuffle-cm.yaml index ad02ad97..1120d99a 100644 --- a/functions/final-specs/final-specs/shuffle-cm.yaml +++ b/functions/final-specs/final-specs/shuffle-cm.yaml @@ -55,6 +55,7 @@ data: IS_KUBERNETES: "true" REGISTRY_URL: "172.17.14.71:5000" #REGISTRY_AUTH: "" + SHUFFLE_KUBERNETES_WORKER: "dhavald055/shuffle-k8s:shuffle-worker-kubernetes" kind: ConfigMap metadata: creationTimestamp: null diff --git a/functions/final-specs/final-specs/shuffle-orborus.yaml b/functions/final-specs/final-specs/shuffle-orborus.yaml index d3b9b713..fd5f0a6f 100644 --- a/functions/final-specs/final-specs/shuffle-orborus.yaml +++ b/functions/final-specs/final-specs/shuffle-orborus.yaml @@ -52,6 +52,11 @@ spec: configMapKeyRef: key: REGISTRY_URL name: env + - name: SHUFFLE_KUBERNETES_WORKER + valueFrom: + configMapKeyRef: + key: SHUFFLE_KUBERNETES_WORKER + name: env image: dhavald055/shuffle-k8s:shuffle-orborus-kubernetes #imagePullPolicy: Never diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index a480b9f4..419f8a38 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -589,7 +589,9 @@ func deployWorker(image string, identifier string, env []string, executionReques env = append(env, fmt.Sprintf("IS_KUBERNETES=%s", os.Getenv("IS_KUBERNETES"))) } - image = "shuffle-worker:v1" //hard coded image name to test locally + image = os.Getenv("SHUFFLE_KUBERNETES_WORKER") + log.Printf("[DEBUG] using worker image:", image) + // image = "shuffle-worker:v1" //hard coded image name to test locally envMap := make(map[string]string) for _, envStr := range env { From e1fcf484d0014493c7bdbb87c45b435fdec9bf29 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Tue, 10 Oct 2023 12:00:16 +0530 Subject: [PATCH 074/160] Update shuffle-opensearch.yaml --- functions/final-specs/final-specs/shuffle-opensearch.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/final-specs/final-specs/shuffle-opensearch.yaml b/functions/final-specs/final-specs/shuffle-opensearch.yaml index 8173064f..75ee74da 100644 --- a/functions/final-specs/final-specs/shuffle-opensearch.yaml +++ b/functions/final-specs/final-specs/shuffle-opensearch.yaml @@ -62,8 +62,8 @@ spec: # securityContext: # runAsUser: 1000 # UID # fsGroup: 1000 # GID - nodeSelector: - node: worker1 + # nodeSelector: + # node: worker1 containers: - env: - name: OPENSEARCH_JAVA_OPTS From a772ee4d8b7b4a64fe2f2315516de230441b7ca0 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Tue, 10 Oct 2023 12:39:23 +0530 Subject: [PATCH 075/160] Update shuffle-backend.yaml --- functions/final-specs/final-specs/shuffle-backend.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/final-specs/final-specs/shuffle-backend.yaml b/functions/final-specs/final-specs/shuffle-backend.yaml index 4da4c4cc..a6ac1e7e 100644 --- a/functions/final-specs/final-specs/shuffle-backend.yaml +++ b/functions/final-specs/final-specs/shuffle-backend.yaml @@ -10,7 +10,7 @@ spec: accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain - storageClassName: fast + storageClassName: shuffle-data hostPath: path: /mnt/shuffle-data/backend From 8802071cc39ba00358cd8fcdf68c65e6286bc028 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Tue, 10 Oct 2023 14:15:53 +0000 Subject: [PATCH 076/160] renamed k8s folder --- functions/k8s-confs/[stage-1 | 0 functions/k8s-confs/backend-deployment.yaml | 434 ------------------ functions/k8s-confs/env-configmap.yaml | 63 --- functions/k8s-confs/frontend-deployment.yaml | 68 --- functions/k8s-confs/kaniko.yaml | 34 -- .../k8s-confs/opensearch-deployment.yaml | 129 ------ functions/k8s-confs/orborus-deployment.yaml | 62 --- functions/k8s-confs/registry-deployment.yaml | 75 --- functions/k8s-confs/shuffle-registry.yaml | 68 --- functions/k8s-confs/shuffle-role.yaml | 12 - functions/k8s-confs/shuffle-rolebinding.yaml | 14 - .../shuffle-backend.yaml | 0 .../shuffle-cm.yaml | 0 .../shuffle-frontend.yaml | 0 .../shuffle-opensearch.yaml | 0 .../shuffle-orborus.yaml | 0 .../shuffle-role.yaml | 0 .../shuffle-rolebinding.yaml | 0 .../shuffle-sc.yaml | 0 19 files changed, 959 deletions(-) delete mode 100644 functions/k8s-confs/[stage-1 delete mode 100644 functions/k8s-confs/backend-deployment.yaml delete mode 100644 functions/k8s-confs/env-configmap.yaml delete mode 100644 functions/k8s-confs/frontend-deployment.yaml delete mode 100644 functions/k8s-confs/kaniko.yaml delete mode 100644 functions/k8s-confs/opensearch-deployment.yaml delete mode 100644 functions/k8s-confs/orborus-deployment.yaml delete mode 100644 functions/k8s-confs/registry-deployment.yaml delete mode 100644 functions/k8s-confs/shuffle-registry.yaml delete mode 100644 functions/k8s-confs/shuffle-role.yaml delete mode 100644 functions/k8s-confs/shuffle-rolebinding.yaml rename functions/{final-specs/final-specs => kubernetes}/shuffle-backend.yaml (100%) rename functions/{final-specs/final-specs => kubernetes}/shuffle-cm.yaml (100%) rename functions/{final-specs/final-specs => kubernetes}/shuffle-frontend.yaml (100%) rename functions/{final-specs/final-specs => kubernetes}/shuffle-opensearch.yaml (100%) rename functions/{final-specs/final-specs => kubernetes}/shuffle-orborus.yaml (100%) rename functions/{final-specs/final-specs => kubernetes}/shuffle-role.yaml (100%) rename functions/{final-specs/final-specs => kubernetes}/shuffle-rolebinding.yaml (100%) rename functions/{final-specs/final-specs => kubernetes}/shuffle-sc.yaml (100%) diff --git a/functions/k8s-confs/[stage-1 b/functions/k8s-confs/[stage-1 deleted file mode 100644 index e69de29b..00000000 diff --git a/functions/k8s-confs/backend-deployment.yaml b/functions/k8s-confs/backend-deployment.yaml deleted file mode 100644 index c6f1dbfc..00000000 --- a/functions/k8s-confs/backend-deployment.yaml +++ /dev/null @@ -1,434 +0,0 @@ ---- - -apiVersion: v1 -kind: PersistentVolume -metadata: - name: shuffle-apps-pv -spec: - capacity: - storage: 10Gi - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: fast - local: - path: /mnt/shuffle-data/backend - nodeAffinity: - required: - nodeSelectorTerms: - - matchExpressions: - - key: node - operator: In - values: - - master - -# --- - -# apiVersion: v1 -# kind: PersistentVolume -# metadata: -# name: shuffle-files-pv -# spec: -# capacity: -# storage: 5Gi -# accessModes: -# - ReadWriteOnce -# persistentVolumeReclaimPolicy: Retain -# storageClassName: fast -# hostPath: -# path: /mnt/shuffle-data/backend - -# --- - -# apiVersion: v1 -# kind: PersistentVolumeClaim -# metadata: -# creationTimestamp: null -# labels: -# io.kompose.service: backend-files-claim -# name: backend-files-claim -# spec: -# accessModes: -# - ReadWriteOnce -# storageClassName: fast -# resources: -# requests: -# storage: 1Gi -# status: {} - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: backend-apps-claim - name: backend-apps-claim -spec: - accessModes: - - ReadWriteOnce - storageClassName: fast - resources: - requests: - storage: 1Gi -# status: {} - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: backend - name: backend -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: backend - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: backend - app: shuffle-backend - name: shuffle-backend - spec: - volumes: - # - name: shuffle-files - # persistentVolumeClaim: - # claimName: backend-files-claim - # - name: shuffle-apps - # hostPath: - # path: /mnt/shuffle-data/backend - # type: DirectoryOrCreate - - name: shuffle-apps - persistentVolumeClaim: - claimName: backend-apps-claim - # - name: docker-socket - # hostPath: - # path: /var/run/docker.sock - nodeSelector: - node: master - containers: - - env: - - name: BACKEND_HOSTNAME - valueFrom: - configMapKeyRef: - key: BACKEND_HOSTNAME - name: env - - name: BACKEND_PORT - valueFrom: - configMapKeyRef: - key: BACKEND_PORT - name: env - - name: BASE_URL - valueFrom: - configMapKeyRef: - key: BASE_URL - name: env - - name: DATASTORE_EMULATOR_HOST - valueFrom: - configMapKeyRef: - key: DATASTORE_EMULATOR_HOST - name: env - - name: DB_LOCATION - valueFrom: - configMapKeyRef: - key: DB_LOCATION - name: env - - name: DOCKER_API_VERSION - valueFrom: - configMapKeyRef: - key: DOCKER_API_VERSION - name: env - - name: ENVIRONMENT_NAME - valueFrom: - configMapKeyRef: - key: ENVIRONMENT_NAME - name: env - - name: FRONTEND_PORT - valueFrom: - configMapKeyRef: - key: FRONTEND_PORT - name: env - - name: FRONTEND_PORT_HTTPS - valueFrom: - configMapKeyRef: - key: FRONTEND_PORT_HTTPS - name: env - - name: HTTPS_PROXY - valueFrom: - configMapKeyRef: - key: HTTPS_PROXY - name: env - - name: HTTP_PROXY - valueFrom: - configMapKeyRef: - key: HTTP_PROXY - name: env - - name: ORBORUS_CONTAINER_NAME - valueFrom: - configMapKeyRef: - key: ORBORUS_CONTAINER_NAME - name: env - - name: ORG_ID - valueFrom: - configMapKeyRef: - key: ORG_ID - name: env - - name: OUTER_HOSTNAME - valueFrom: - configMapKeyRef: - key: OUTER_HOSTNAME - name: env - - name: SHUFFLE_APP_DOWNLOAD_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_DOWNLOAD_LOCATION - name: env - - name: SHUFFLE_APP_FORCE_UPDATE - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_FORCE_UPDATE - name: env - - name: SHUFFLE_APP_HOTLOAD_FOLDER - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_HOTLOAD_FOLDER - name: env - - name: SHUFFLE_APP_HOTLOAD_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_HOTLOAD_LOCATION - name: env - - name: SHUFFLE_BASE_IMAGE_NAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_BASE_IMAGE_NAME - name: env - - name: SHUFFLE_BASE_IMAGE_REGISTRY - valueFrom: - configMapKeyRef: - key: SHUFFLE_BASE_IMAGE_REGISTRY - name: env - - name: SHUFFLE_BASE_IMAGE_TAG_SUFFIX - valueFrom: - configMapKeyRef: - key: SHUFFLE_BASE_IMAGE_TAG_SUFFIX - name: env - - name: SHUFFLE_CHAT_DISABLED - valueFrom: - configMapKeyRef: - key: SHUFFLE_CHAT_DISABLED - name: env - - name: SHUFFLE_CONTAINER_AUTO_CLEANUP - valueFrom: - configMapKeyRef: - key: SHUFFLE_CONTAINER_AUTO_CLEANUP - name: env - - name: SHUFFLE_DEFAULT_APIKEY - valueFrom: - configMapKeyRef: - key: SHUFFLE_DEFAULT_APIKEY - name: env - - name: SHUFFLE_DEFAULT_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_DEFAULT_PASSWORD - name: env - - name: SHUFFLE_DEFAULT_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_DEFAULT_USERNAME - name: env - - name: SHUFFLE_DOWNLOAD_AUTH_BRANCH - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_AUTH_BRANCH - name: env - - name: SHUFFLE_DOWNLOAD_AUTH_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_AUTH_PASSWORD - name: env - - name: SHUFFLE_DOWNLOAD_AUTH_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_AUTH_USERNAME - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME - name: env - - name: SHUFFLE_ELASTIC - valueFrom: - configMapKeyRef: - key: SHUFFLE_ELASTIC - name: env - - name: SHUFFLE_ENCRYPTION_MODIFIER - valueFrom: - configMapKeyRef: - key: SHUFFLE_ENCRYPTION_MODIFIER - name: env - - name: SHUFFLE_FILE_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_FILE_LOCATION - name: env - - name: SHUFFLE_LOGS_DISABLED - valueFrom: - configMapKeyRef: - key: SHUFFLE_LOGS_DISABLED - name: env - - name: SHUFFLE_OPENSEARCH_APIKEY - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_APIKEY - name: env - - name: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE - name: env - - name: SHUFFLE_OPENSEARCH_CLOUDID - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_CLOUDID - name: env - - name: SHUFFLE_OPENSEARCH_INDEX_PREFIX - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_INDEX_PREFIX - name: env - - name: SHUFFLE_OPENSEARCH_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_PASSWORD - name: env - - name: SHUFFLE_OPENSEARCH_PROXY - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_PROXY - name: env - - name: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY - name: env - - name: SHUFFLE_OPENSEARCH_URL - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_URL - name: env - - name: SHUFFLE_OPENSEARCH_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_USERNAME - name: env - - name: SHUFFLE_ORBORUS_STARTUP_DELAY - valueFrom: - configMapKeyRef: - key: SHUFFLE_ORBORUS_STARTUP_DELAY - name: env - - name: SHUFFLE_PASS_APP_PROXY - valueFrom: - configMapKeyRef: - key: SHUFFLE_PASS_APP_PROXY - name: env - - name: SHUFFLE_PASS_WORKER_PROXY - valueFrom: - configMapKeyRef: - key: SHUFFLE_PASS_WORKER_PROXY - name: env - - name: SHUFFLE_RERUN_SCHEDULE - valueFrom: - configMapKeyRef: - key: SHUFFLE_RERUN_SCHEDULE - name: env - - name: SSO_REDIRECT_URL - valueFrom: - configMapKeyRef: - key: SSO_REDIRECT_URL - name: env - - name: TZ - valueFrom: - configMapKeyRef: - key: TZ - name: env - - name: IS_KUBERNETES - valueFrom: - configMapKeyRef: - key: IS_KUBERNETES - name: env - - name: REGISTRY_URL - valueFrom: - configMapKeyRef: - key: REGISTRY_URL - name: env - image: shuffle-backend:v1 - imagePullPolicy: Never - name: shuffle-backend - ports: - - containerPort: 5001 - resources: {} - volumeMounts: - # - name: docker-socket - # mountPath: /var/run/docker.sock - - name: shuffle-apps - mountPath: /app/generated - # - name: shuffle-files - # mountPath: /shuffle-files - restartPolicy: Always -status: {} - ---- - -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: backend - name: shuffle-backend -spec: - type: NodePort - ports: - - name: "50001" - port: 5001 - targetPort: 5001 - nodePort: 30009 - selector: - io.kompose.service: backend -status: - loadBalancer: {} - diff --git a/functions/k8s-confs/env-configmap.yaml b/functions/k8s-confs/env-configmap.yaml deleted file mode 100644 index ad02ad97..00000000 --- a/functions/k8s-confs/env-configmap.yaml +++ /dev/null @@ -1,63 +0,0 @@ -apiVersion: v1 -data: - BACKEND_HOSTNAME: shuffle-backend - BACKEND_PORT: "5001" - BASE_URL: http://shuffle-backend:5001 - DATASTORE_EMULATOR_HOST: shuffle-database:8000 - DB_LOCATION: /mnt/shuffle-data/open-search - DOCKER_API_VERSION: "1.40" - ENVIRONMENT_NAME: Shuffle - FRONTEND_PORT: "3001" - FRONTEND_PORT_HTTPS: "3443" - HTTP_PROXY: "" - HTTPS_PROXY: "" - ORBORUS_CONTAINER_NAME: "\t\t\t\t" - ORG_ID: Shuffle - OUTER_HOSTNAME: shuffle-backend - SHUFFLE_APP_DOWNLOAD_LOCATION: https://github.com/shuffle/python-apps - SHUFFLE_APP_FORCE_UPDATE: "false" - SHUFFLE_APP_HOTLOAD_FOLDER: /shuffle-apps - SHUFFLE_APP_HOTLOAD_LOCATION: ./shuffle-apps - SHUFFLE_BASE_IMAGE_NAME: shuffle - SHUFFLE_BASE_IMAGE_REGISTRY: ghcr.io - SHUFFLE_BASE_IMAGE_TAG_SUFFIX: -1.0.0 - SHUFFLE_CHAT_DISABLED: "false" - SHUFFLE_CONTAINER_AUTO_CLEANUP: "false" - SHUFFLE_DEFAULT_APIKEY: "" - SHUFFLE_DEFAULT_PASSWORD: "" - SHUFFLE_DEFAULT_USERNAME: "" - SHUFFLE_DOWNLOAD_AUTH_BRANCH: "" - SHUFFLE_DOWNLOAD_AUTH_PASSWORD: "" - SHUFFLE_DOWNLOAD_AUTH_USERNAME: "" - SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH: "" - SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION: "" - SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD: "" - SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME: "" - SHUFFLE_ELASTIC: "true" - SHUFFLE_ENCRYPTION_MODIFIER: "" - SHUFFLE_FILE_LOCATION: /shuffle-files - SHUFFLE_LOGS_DISABLED: "false" - SHUFFLE_OPENSEARCH_APIKEY: "" - SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: "" - SHUFFLE_OPENSEARCH_CLOUDID: "" - SHUFFLE_OPENSEARCH_INDEX_PREFIX: "" - SHUFFLE_OPENSEARCH_PASSWORD: admin - SHUFFLE_OPENSEARCH_PROXY: "" - SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: "true" - SHUFFLE_OPENSEARCH_URL: https://opensearch:9200 - SHUFFLE_OPENSEARCH_USERNAME: admin - SHUFFLE_ORBORUS_STARTUP_DELAY: "\t\t" - SHUFFLE_PASS_APP_PROXY: "FALSE" - SHUFFLE_PASS_WORKER_PROXY: "TRUE" - SHUFFLE_RERUN_SCHEDULE: "300" - SSO_REDIRECT_URL: "" - TZ: "Europe/Amsterdam \t\t\t\t\t" - IS_KUBERNETES: "true" - REGISTRY_URL: "172.17.14.71:5000" - #REGISTRY_AUTH: "" -kind: ConfigMap -metadata: - creationTimestamp: null - labels: - io.kompose.service: backend-env - name: env diff --git a/functions/k8s-confs/frontend-deployment.yaml b/functions/k8s-confs/frontend-deployment.yaml deleted file mode 100644 index cb11f728..00000000 --- a/functions/k8s-confs/frontend-deployment.yaml +++ /dev/null @@ -1,68 +0,0 @@ ---- - -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: frontend - name: frontend -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: frontend - strategy: {} - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: frontend - spec: - containers: - - env: - - name: BACKEND_HOSTNAME - image: ghcr.io/shuffle/shuffle-frontend:latest - name: shuffle-frontend - ports: - - containerPort: 80 - - containerPort: 443 - resources: {} - hostname: shuffle-frontend - restartPolicy: Always -status: {} - ---- - -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: frontend - name: frontend -spec: - type: NodePort - ports: - - name: "80" - port: 80 - targetPort: 80 - nodePort: 30007 - - name: "443" - port: 443 - targetPort: 443 - nodePort: 30008 - selector: - io.kompose.service: frontend -# status: -# loadBalancer: {} diff --git a/functions/k8s-confs/kaniko.yaml b/functions/k8s-confs/kaniko.yaml deleted file mode 100644 index bbeb261e..00000000 --- a/functions/k8s-confs/kaniko.yaml +++ /dev/null @@ -1,34 +0,0 @@ - -apiVersion: batch/v1 -kind: Job -metadata: - name: kaniko-build -spec: - template: - spec: - nodeSelector: - node: master - containers: - - name: kaniko - image: gcr.io/kaniko-project/executor:debug - # command: ["/busybox/sh", "-c"] - # args: ["-c","while true; do sleep 3600; done"] - args: [ - "--verbosity=debug", - "--context=dir:///app/generated/ok-349ec164d4ad2409152dcb901257f718", - "--dockerfile=./Dockerfile", - #"--no-push", - "--skip-tls-verify", - # "--registry-certificate=172.17.14.71:5000=/workspace/certs/cert.pem", - "--destination=172.17.14.71:5000/shuffle/dhaval-kaniko:1.0" - ] - volumeMounts: - - name: kaniko-workspace - mountPath: /app/generated - nodeSelector: - node: master - restartPolicy: Never - volumes: - - name: kaniko-workspace - persistentVolumeClaim: - claimName: backend-apps-claim diff --git a/functions/k8s-confs/opensearch-deployment.yaml b/functions/k8s-confs/opensearch-deployment.yaml deleted file mode 100644 index 4d0e996a..00000000 --- a/functions/k8s-confs/opensearch-deployment.yaml +++ /dev/null @@ -1,129 +0,0 @@ ---- - -apiVersion: v1 -kind: PersistentVolume -metadata: - name: shuffle-os-pv -spec: - capacity: - storage: 10Gi # Adjust the storage size as per your requirements - accessModes: - - ReadWriteOnce # This allows read-write access to a single node - persistentVolumeReclaimPolicy: Retain # Adjust the reclaim policy as per your needs - storageClassName: fast # Set the desired storage class - hostPath: - path: /mnt/shuffle-data/open-search - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: opensearch-claim0 - name: opensearch-claim0 -spec: - accessModes: - - ReadWriteOnce - storageClassName: fast - resources: - requests: - storage: 500Mi -status: {} - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: opensearch - name: opensearch -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: opensearch - strategy: {} - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: opensearch - spec: - # securityContext: - # runAsUser: 1000 # UID - # fsGroup: 1000 # GID - nodeSelector: - node: worker1 - containers: - - env: - - name: OPENSEARCH_JAVA_OPTS - value: -Xms1024m -Xmx1024m - #- name: bootstrap.memory_lock - #value: "true" - - name: cluster.initial_master_nodes - value: shuffle-opensearch - - name: cluster.name - value: shuffle-cluster - - name: cluster.routing.allocation.disk.threshold_enabled - value: "false" - - name: discovery.seed_hosts - value: shuffle-opensearch - - name: node.name - value: shuffle-opensearch - - name: node.store.allow_mmap - value: "false" - - name: DB_LOCATION - valueFrom: - configMapKeyRef: - name: env - key: DB_LOCATION - image: opensearchproject/opensearch:2.5.0 - name: shuffle-opensearch - ports: - - containerPort: 9200 - resources: {} - volumeMounts: - - mountPath: /usr/share/opensearch/data - name: opensearch-claim0 - hostname: shuffle-opensearch - restartPolicy: Always - volumes: - - name: opensearch-claim0 - persistentVolumeClaim: - claimName: opensearch-claim0 -status: {} - ---- - -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: opensearch - name: opensearch -spec: - type: NodePort - ports: - - name: "9200" - port: 9200 - targetPort: 9200 - nodePort: 31001 - selector: - io.kompose.service: opensearch -status: - loadBalancer: {} - diff --git a/functions/k8s-confs/orborus-deployment.yaml b/functions/k8s-confs/orborus-deployment.yaml deleted file mode 100644 index 05ebfe08..00000000 --- a/functions/k8s-confs/orborus-deployment.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: orborus - name: orborus -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: orborus - strategy: {} - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: orborus - spec: - containers: - - env: - - name: BASE_URL - value: "http://shuffle-backend:5001" - - name: DOCKER_API_VERSION - value: "1.40" - - name: ENVIRONMENT_NAME - value: Shuffle - - name: ORG_ID - value: Shuffle - - name: SHUFFLE_APP_SDK_VERSION - value: nightly - - name: SHUFFLE_SCALE_REPLICAS - value: "5" - #- name: SHUFFLE_SWARM_CONFIG - #value: run - - name: SHUFFLE_WORKER_VERSION - value: nightly - - name: IS_KUBERNETES - valueFrom: - configMapKeyRef: - key: IS_KUBERNETES - name: env - - name: REGISTRY_URL - valueFrom: - configMapKeyRef: - key: REGISTRY_URL - name: env - - image: shuffle-orborus:v1 - imagePullPolicy: Never - name: shuffle-orborus - resources: {} - hostname: shuffle-orborus - restartPolicy: Always -status: {} diff --git a/functions/k8s-confs/registry-deployment.yaml b/functions/k8s-confs/registry-deployment.yaml deleted file mode 100644 index c7d3044d..00000000 --- a/functions/k8s-confs/registry-deployment.yaml +++ /dev/null @@ -1,75 +0,0 @@ ---- - -apiVersion: v1 -kind: PersistentVolume -metadata: - name: registry-pv -spec: - capacity: - storage: 10Gi - accessModes: - - ReadWriteMany - persistentVolumeReclaimPolicy: Retain - storageClassName: local-storage - hostPath: - path: "/mnt/shuffle-data/registry" - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: registry-pvc -spec: - accessModes: - - ReadWriteMany - resources: - requests: - storage: 10Gi - storageClassName: local-storage - ---- - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: shuffle-registry -spec: - replicas: 1 - selector: - matchLabels: - app: registry - template: - metadata: - labels: - app: registry - spec: - containers: - - name: registry - image: registry:2 - ports: - - containerPort: 5000 - volumeMounts: - - name: registry-data - mountPath: /var/lib/registry - volumes: - - name: registry-data - persistentVolumeClaim: - claimName: registry-pvc ---- - -# registry-service.yaml -apiVersion: v1 -kind: Service -metadata: - name: shuffle-registry -spec: - selector: - app: registry - #type: NodePort - ports: - - protocol: TCP - port: 5000 - targetPort: 5000 - - \ No newline at end of file diff --git a/functions/k8s-confs/shuffle-registry.yaml b/functions/k8s-confs/shuffle-registry.yaml deleted file mode 100644 index c6a80858..00000000 --- a/functions/k8s-confs/shuffle-registry.yaml +++ /dev/null @@ -1,68 +0,0 @@ ---- - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: shuffle-registry -spec: - replicas: 1 - selector: - matchLabels: - app: shuffle-registry - template: - metadata: - labels: - app: shuffle-registry - spec: - initContainers: - - name: generate-certs - image: frapsoft/openssl - command: ["openssl"] - args: - - req - - -newkey - - rsa:4096 - - -nodes - - -sha256 - - -x509 - - -days - - "365" - - -keyout - - /certs/domain.key - - -out - - /certs/domain.crt - - -subj - - "/CN=shuffle-registry.default.svc.cluster.local" - volumeMounts: - - name: certs - mountPath: /certs - containers: - - name: registry - image: registry:2 - ports: - - containerPort: 5000 - env: - - name: REGISTRY_HTTP_TLS_CERTIFICATE - value: "/certs/domain.crt" - - name: REGISTRY_HTTP_TLS_KEY - value: "/certs/domain.key" - volumeMounts: - - name: certs - mountPath: "/certs" - volumes: - - name: certs - emptyDir: {} - ---- - -apiVersion: v1 -kind: Service -metadata: - name: shuffle-registry -spec: - selector: - app: shuffle-registry - ports: - - protocol: TCP - port: 5000 - targetPort: 5000 diff --git a/functions/k8s-confs/shuffle-role.yaml b/functions/k8s-confs/shuffle-role.yaml deleted file mode 100644 index bf2afbe9..00000000 --- a/functions/k8s-confs/shuffle-role.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - namespace: shuffle - name: pod-manager -rules: -- apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "create", "update", "delete"] -- apiGroups: ["batch"] - resources: ["jobs"] - verbs: ["create", "get", "list", "watch", "delete"] diff --git a/functions/k8s-confs/shuffle-rolebinding.yaml b/functions/k8s-confs/shuffle-rolebinding.yaml deleted file mode 100644 index fd2225f6..00000000 --- a/functions/k8s-confs/shuffle-rolebinding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: pod-manager-binding - namespace: shuffle -subjects: -- kind: ServiceAccount - name: default - namespace: shuffle -roleRef: - kind: Role - name: pod-manager - apiGroup: rbac.authorization.k8s.io - diff --git a/functions/final-specs/final-specs/shuffle-backend.yaml b/functions/kubernetes/shuffle-backend.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-backend.yaml rename to functions/kubernetes/shuffle-backend.yaml diff --git a/functions/final-specs/final-specs/shuffle-cm.yaml b/functions/kubernetes/shuffle-cm.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-cm.yaml rename to functions/kubernetes/shuffle-cm.yaml diff --git a/functions/final-specs/final-specs/shuffle-frontend.yaml b/functions/kubernetes/shuffle-frontend.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-frontend.yaml rename to functions/kubernetes/shuffle-frontend.yaml diff --git a/functions/final-specs/final-specs/shuffle-opensearch.yaml b/functions/kubernetes/shuffle-opensearch.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-opensearch.yaml rename to functions/kubernetes/shuffle-opensearch.yaml diff --git a/functions/final-specs/final-specs/shuffle-orborus.yaml b/functions/kubernetes/shuffle-orborus.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-orborus.yaml rename to functions/kubernetes/shuffle-orborus.yaml diff --git a/functions/final-specs/final-specs/shuffle-role.yaml b/functions/kubernetes/shuffle-role.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-role.yaml rename to functions/kubernetes/shuffle-role.yaml diff --git a/functions/final-specs/final-specs/shuffle-rolebinding.yaml b/functions/kubernetes/shuffle-rolebinding.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-rolebinding.yaml rename to functions/kubernetes/shuffle-rolebinding.yaml diff --git a/functions/final-specs/final-specs/shuffle-sc.yaml b/functions/kubernetes/shuffle-sc.yaml similarity index 100% rename from functions/final-specs/final-specs/shuffle-sc.yaml rename to functions/kubernetes/shuffle-sc.yaml From 5c330b1151eab2f70b11aa042e61b9c67833cf47 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Tue, 10 Oct 2023 14:39:08 +0530 Subject: [PATCH 077/160] Create kubernetes.md --- functions/kubernetes/kubernetes.md | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 functions/kubernetes/kubernetes.md diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md new file mode 100644 index 00000000..996b1d55 --- /dev/null +++ b/functions/kubernetes/kubernetes.md @@ -0,0 +1,38 @@ +## How to deploy Shuffle on Kubernetes? + +### Prerequisites: +- Clone the https://github.com/dhaval055/shuffle repository using Git and change the branch to shuffle-k8s navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. +- Running Kubernetes cluster. +- Run the following command for setting up Opensearch and Backend volumes. +- Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. + +``` +cd /mnt +mkdir shuffle-data +cd shuffle-data +mkdir open-search +mkdir backend +sudo chown –R 1000:1000 open-search +``` + +Step 1: Create a namespace called shuffle in a cluster by running ```kubectl create ns shuffle```. + +Step 2: Open the shuffle-cm.yaml file and review the configuration values. Change the value of REGISTRY_URL with your registry URL. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap with ‘kubectl apply –f shuffle-cm.yaml –n shuffle’. + +Step 3: Apply shuffle-role.yaml by using ```kubectl apply –f shuffle-role.yaml –n shuffle```. + +Step 4: Subsequently, establish the role bindings to associate the roles with the appropriate service accounts by applying the shuffle-rolebinding.yaml configuration using ```kubectl apply –f shuffle-rolebinding.yaml –n shuffle```. + +Step 5: Create storage-class for shuffle by running ```kubectl apply –f shuffle-sc.yaml –n shuffle```. + +Step 6: Apply the shuffle-opensearch.yaml configuration to create a Persistent Volume Claim (PVC) for storage, a Deployment to manage the OpenSearch pod, and a Service to expose OpenSearch within the cluster by using ```kubectl apply –f shuffle-opensearch.yaml –n shuffle```. + +Step 7: Now deploy the backend using ```kubectl apply –f shuffle-backend.yaml –n shuffle```. + +Step 8: Ensure that the Shuffle Backend is successfully deployed and running before proceeding with the frontend deployment, as the frontend may depend on services provided by the backend. ```kubectl apply –f shuffle-frontend.yaml –n shuffle```. + +Step 9: Run ```kubectl apply -f shuffle-orborus.yaml```. This final step ensures that Shuffle Orborus is deployed, enabling the workflow executions in your Kubernetes-based Shuffle setup. + +Now, open https://:30008. You should be seeing a signup page. NODE_IP should be where the frontend is deployed. + + From dee6436364ef2e3ee4842eaebec2f4afddeb0f9d Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Tue, 10 Oct 2023 15:17:54 +0530 Subject: [PATCH 078/160] Update kubernetes.md --- functions/kubernetes/kubernetes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index 996b1d55..576c645e 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -1,7 +1,7 @@ ## How to deploy Shuffle on Kubernetes? ### Prerequisites: -- Clone the https://github.com/dhaval055/shuffle repository using Git and change the branch to shuffle-k8s navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. +- Clone the https://github.com/dhaval055/shuffle repository using Git and change the branch to shuffle-k8s then navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. - Running Kubernetes cluster. - Run the following command for setting up Opensearch and Backend volumes. - Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. @@ -33,6 +33,6 @@ Step 8: Ensure that the Shuffle Backend is successfully deployed and running bef Step 9: Run ```kubectl apply -f shuffle-orborus.yaml```. This final step ensures that Shuffle Orborus is deployed, enabling the workflow executions in your Kubernetes-based Shuffle setup. -Now, open https://:30008. You should be seeing a signup page. NODE_IP should be where the frontend is deployed. +Now, open ```https://:30008``` or ```http://:30007```. You should be seeing a signup page. NODE_IP should be where the frontend is deployed. From 5a215949fd4a6b45295326e7a28613dd28efc18b Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Tue, 10 Oct 2023 15:26:57 +0530 Subject: [PATCH 079/160] Update kubernetes.md --- functions/kubernetes/kubernetes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index 576c645e..890ef51a 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -4,7 +4,7 @@ - Clone the https://github.com/dhaval055/shuffle repository using Git and change the branch to shuffle-k8s then navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. - Running Kubernetes cluster. - Run the following command for setting up Opensearch and Backend volumes. -- Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. +- Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. If you're using Minikube for testing, run commands in box below after running ```minikube ssh```. ``` cd /mnt From c8a5e8be86ebb49424a75bb67bbc65d7af81634e Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Wed, 11 Oct 2023 09:20:22 +0530 Subject: [PATCH 080/160] fix: adding stats endpoint --- backend/go-app/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ce2a0b42..8a4de4de 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5894,6 +5894,8 @@ func initHandlers() { r.HandleFunc("/api/v1/_ah/health", shuffle.HealthCheckHandler) r.HandleFunc("/api/v1/health", shuffle.RunOpsHealthCheck).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/health/stats", shuffle.GetOpsDashboardStats).Methods("GET", "OPTIONS") + // Make user related locations // Fix user changes with org r.HandleFunc("/api/v1/users/login", shuffle.HandleLogin).Methods("POST", "OPTIONS") From 9988a34b47858089d438cc4684d106cd5718c4f7 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Thu, 12 Oct 2023 12:51:41 +0000 Subject: [PATCH 081/160] fixed local registry part in wokrer --- functions/onprem/worker/worker.go | 196 +++++++++++++++--------------- 1 file changed, 100 insertions(+), 96 deletions(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index cc7dcbc4..3770d6b3 100755 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -233,6 +233,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] if os.Getenv("IS_KUBERNETES") == "true" { namespace := "shuffle" + localRegistry := os.Getenv("REGISTRY_URL") envMap := make(map[string]string) for _, envStr := range env { @@ -257,8 +258,10 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] // checking if app is generated or not appDetails := strings.Split(image, ":")[1] appDetailsSplit := strings.Split(appDetails, "_") - appName := appDetailsSplit[0] - appVersion := appDetailsSplit[1] + appName := strings.Join(appDetailsSplit[:len(appDetailsSplit)-1], "_") + appVersion := appDetailsSplit[len(appDetailsSplit)-1] + + log.Printf("APP VERSION IS: %s", appVersion) for _, app := range workflowExecution.Workflow.Actions { log.Printf("[DEBUG] App: %s, Version: %s", appName, appVersion) @@ -266,7 +269,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] if app.AppName == appName && app.AppVersion == appVersion { if app.Generated == true { log.Printf("[DEBUG] Generated app, setting local registry") - image = fmt.Sprintf("%s/%s", registryName, image) + image = fmt.Sprintf("%s/%s", localRegistry, image) break } else { log.Printf("[DEBUG] Not generated app, setting shuffle registry") @@ -287,6 +290,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] }, }, Spec: corev1.PodSpec{ + NodeName: "worker1", // change this in prod RestartPolicy: "Never", Containers: []corev1.Container{ { @@ -301,114 +305,114 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] createdPod, err := clientset.CoreV1().Pods(namespace).Create(context.Background(), pod, metav1.CreateOptions{}) if err != nil { fmt.Fprintf(os.Stderr, "Error creating pod: %v\n", err) - os.Exit(1) + // os.Exit(1) } fmt.Printf("[DEBUG] Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace) } else { // form basic hostConfig - ctx := context.Background() + ctx := context.Background() - if action.AppName == "shuffle-subflow" { - // Automatic replacement of URL - for paramIndex, param := range action.Parameters { - if param.Name != "backend_url" { - continue - } + if action.AppName == "shuffle-subflow" { + // Automatic replacement of URL + for paramIndex, param := range action.Parameters { + if param.Name != "backend_url" { + continue + } - if strings.Contains(param.Value, "shuffle-backend") { - // Automatic replacement as this is default - action.Parameters[paramIndex].Value = os.Getenv("BASE_URL") - log.Printf("[DEBUG][%s] Replaced backend_url with %s", workflowExecution.ExecutionId, os.Getenv("BASE_URL")) + if strings.Contains(param.Value, "shuffle-backend") { + // Automatic replacement as this is default + action.Parameters[paramIndex].Value = os.Getenv("BASE_URL") + log.Printf("[DEBUG][%s] Replaced backend_url with %s", workflowExecution.ExecutionId, os.Getenv("BASE_URL")) + } } } - } - // Max 10% CPU every second - //CPUShares: 128, - //CPUQuota: 10000, - //CPUPeriod: 100000, - hostConfig := &container.HostConfig{ - LogConfig: container.LogConfig{ - Type: "json-file", - Config: map[string]string{ - "max-size": "10m", + // Max 10% CPU every second + //CPUShares: 128, + //CPUQuota: 10000, + //CPUPeriod: 100000, + hostConfig := &container.HostConfig{ + LogConfig: container.LogConfig{ + Type: "json-file", + Config: map[string]string{ + "max-size": "10m", + }, }, - }, - Resources: container.Resources{}, - } - - hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:worker-%s", workflowExecution.ExecutionId)) - - // Removing because log extraction should happen first - if cleanupEnv == "true" { - hostConfig.AutoRemove = true - } - - // FIXME: Add proper foldermounts here - //log.Printf("\n\nPRE FOLDERMOUNT\n\n") - //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} - //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} - volumeBinds := []string{} - if len(volumeBinds) > 0 { - log.Printf("[DEBUG] Setting up binds for container!") - hostConfig.Binds = volumeBinds - hostConfig.Mounts = []mount.Mount{} - for _, bind := range volumeBinds { - if !strings.Contains(bind, ":") || strings.Contains(bind, "..") || strings.HasPrefix(bind, "~") { - log.Printf("[WARNING] Bind %s is invalid.", bind) - continue - } - - log.Printf("[DEBUG] Appending bind %s", bind) - bindSplit := strings.Split(bind, ":") - sourceFolder := bindSplit[0] - destinationFolder := bindSplit[0] - hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ - Type: mount.TypeBind, - Source: sourceFolder, - Target: destinationFolder, - }) + Resources: container.Resources{}, } - } else { - //log.Printf("[WARNING] Not mounting folders") - } - config := &container.Config{ - Image: image, - Env: env, - } + hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:worker-%s", workflowExecution.ExecutionId)) - // Checking as late as possible, just in case. - newExecId := fmt.Sprintf("%s_%s", workflowExecution.ExecutionId, action.ID) - _, err := shuffle.GetCache(ctx, newExecId) - if err == nil { - log.Printf("\n\n[DEBUG] Result for %s already found - returning\n\n", newExecId) + // Removing because log extraction should happen first + if cleanupEnv == "true" { + hostConfig.AutoRemove = true + } + + // FIXME: Add proper foldermounts here + //log.Printf("\n\nPRE FOLDERMOUNT\n\n") + //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} + //volumeBinds := []string{"/tmp/shuffle-mount:/rules"} + volumeBinds := []string{} + if len(volumeBinds) > 0 { + log.Printf("[DEBUG] Setting up binds for container!") + hostConfig.Binds = volumeBinds + hostConfig.Mounts = []mount.Mount{} + for _, bind := range volumeBinds { + if !strings.Contains(bind, ":") || strings.Contains(bind, "..") || strings.HasPrefix(bind, "~") { + log.Printf("[WARNING] Bind %s is invalid.", bind) + continue + } + + log.Printf("[DEBUG] Appending bind %s", bind) + bindSplit := strings.Split(bind, ":") + sourceFolder := bindSplit[0] + destinationFolder := bindSplit[0] + hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ + Type: mount.TypeBind, + Source: sourceFolder, + Target: destinationFolder, + }) + } + } else { + //log.Printf("[WARNING] Not mounting folders") + } + + config := &container.Config{ + Image: image, + Env: env, + } + + // Checking as late as possible, just in case. + newExecId := fmt.Sprintf("%s_%s", workflowExecution.ExecutionId, action.ID) + _, err := shuffle.GetCache(ctx, newExecId) + if err == nil { + log.Printf("\n\n[DEBUG] Result for %s already found - returning\n\n", newExecId) + return nil + } + + cacheData := []byte("1") + err = shuffle.SetCache(ctx, newExecId, cacheData, 30) + if err != nil { + log.Printf("[WARNING] Failed setting cache for action %s: %s", newExecId, err) + } else { + log.Printf("[DEBUG] Adding %s to cache. Name: %s", newExecId, action.Name) + } + + if action.ExecutionDelay > 0 { + log.Printf("[DEBUG] Running app %s in docker with delay of %d", action.Name, action.ExecutionDelay) + waitTime := time.Duration(action.ExecutionDelay) * time.Second + + time.AfterFunc(waitTime, func() { + DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) + }) + } else { + log.Printf("[DEBUG] Running app %s in docker NORMALLY as there is no delay set with identifier %s", action.Name, identifier) + returnvalue := DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) + log.Printf("[DEBUG] Normal deploy ret: %s", returnvalue) + return returnvalue + } return nil } - - cacheData := []byte("1") - err = shuffle.SetCache(ctx, newExecId, cacheData, 30) - if err != nil { - log.Printf("[WARNING] Failed setting cache for action %s: %s", newExecId, err) - } else { - log.Printf("[DEBUG] Adding %s to cache. Name: %s", newExecId, action.Name) - } - - if action.ExecutionDelay > 0 { - log.Printf("[DEBUG] Running app %s in docker with delay of %d", action.Name, action.ExecutionDelay) - waitTime := time.Duration(action.ExecutionDelay) * time.Second - - time.AfterFunc(waitTime, func() { - DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) - }) - } else { - log.Printf("[DEBUG] Running app %s in docker NORMALLY as there is no delay set with identifier %s", action.Name, identifier) - returnvalue := DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) - log.Printf("[DEBUG] Normal deploy ret: %s", returnvalue) - return returnvalue - } - return nil - } return nil } From 035211e947acfd10aedc264d866b4ed5553e1a2c Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Thu, 12 Oct 2023 15:25:26 +0000 Subject: [PATCH 082/160] registry auth configuration --- functions/kubernetes/shuffle-backend.yaml | 5 +++++ functions/kubernetes/shuffle-cm.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/functions/kubernetes/shuffle-backend.yaml b/functions/kubernetes/shuffle-backend.yaml index a6ac1e7e..6388b1d9 100644 --- a/functions/kubernetes/shuffle-backend.yaml +++ b/functions/kubernetes/shuffle-backend.yaml @@ -384,6 +384,11 @@ spec: configMapKeyRef: key: REGISTRY_URL name: env + - name: REGISTRY_AUTH + valueFrom: + configMapKeyRef: + key: REGISTRY_AUTH + name: env image: dhavald055/shuffle-k8s:shuffle-backend-kubernetes #imagePullPolicy: Never name: shuffle-backend diff --git a/functions/kubernetes/shuffle-cm.yaml b/functions/kubernetes/shuffle-cm.yaml index 1120d99a..cf3b6641 100644 --- a/functions/kubernetes/shuffle-cm.yaml +++ b/functions/kubernetes/shuffle-cm.yaml @@ -54,7 +54,7 @@ data: TZ: "Europe/Amsterdam \t\t\t\t\t" IS_KUBERNETES: "true" REGISTRY_URL: "172.17.14.71:5000" - #REGISTRY_AUTH: "" + REGISTRY_AUTH: "true" #set it to true if you are using registry with authentication SHUFFLE_KUBERNETES_WORKER: "dhavald055/shuffle-k8s:shuffle-worker-kubernetes" kind: ConfigMap metadata: From bea68c709cb8c6c6d7e8d94600207ffd5c97d692 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Thu, 12 Oct 2023 16:24:37 +0530 Subject: [PATCH 083/160] removed comments in worker.go --- functions/onprem/worker/worker.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 3770d6b3..9221a2fd 100755 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -225,9 +225,9 @@ func getKubernetesClient() (*kubernetes.Clientset, error) { // Deploys the internal worker whenever something happens func deployApp(cli *dockerclient.Client, image string, identifier string, env []string, workflowExecution shuffle.WorkflowExecution, action shuffle.Action) error { - log.Printf("################################### new call to deployApp ###################################") - log.Printf("image: %s", image) - log.Printf("identifier: %s", identifier) + // log.Printf("################################### new call to deployApp ###################################") + // log.Printf("image: %s", image) + // log.Printf("identifier: %s", identifier) // log.Printf("execution: %+v", workflowExecution) if os.Getenv("IS_KUBERNETES") == "true" { @@ -246,7 +246,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] clientset, err := getKubernetesClient() if err != nil { fmt.Println("[ERROR]Error getting kubernetes client:", err) - os.Exit(1) + // os.Exit(1) } log.Printf("[DEBUG] Got kubernetes client") @@ -264,8 +264,8 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] log.Printf("APP VERSION IS: %s", appVersion) for _, app := range workflowExecution.Workflow.Actions { - log.Printf("[DEBUG] App: %s, Version: %s", appName, appVersion) - log.Printf("[DEBUG] Checking app %s with version %s", app.AppName, app.AppVersion) + // log.Printf("[DEBUG] App: %s, Version: %s", appName, appVersion) + // log.Printf("[DEBUG] Checking app %s with version %s", app.AppName, app.AppVersion) if app.AppName == appName && app.AppVersion == appVersion { if app.Generated == true { log.Printf("[DEBUG] Generated app, setting local registry") @@ -290,7 +290,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] }, }, Spec: corev1.PodSpec{ - NodeName: "worker1", // change this in prod + // NodeName: "worker1" RestartPolicy: "Never", Containers: []corev1.Container{ { From 8a4e8b61c04060cc2fbfd499d4aa6e950c1e0ee6 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Thu, 12 Oct 2023 16:27:05 +0530 Subject: [PATCH 084/160] removed comments from orborus.go --- functions/onprem/orborus/orborus.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 5d50e0f2..7317bc71 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -581,8 +581,8 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar { func deployWorker(image string, identifier string, env []string, executionRequest shuffle.ExecutionRequest) error { if os.Getenv("IS_KUBERNETES") == "true" { - log.Printf("IS_KUBERNETS", os.Getenv("IS_KUBERNETES")) - log.Printf("REGISTRY_URL", os.Getenv("REGISTRY_URL")) + // log.Printf("IS_KUBERNETS", os.Getenv("IS_KUBERNETES")) + // log.Printf("REGISTRY_URL", os.Getenv("REGISTRY_URL")) if len(os.Getenv("REGISTRY_URL")) > 0 && os.Getenv("REGISTRY_URL") != "" { env = append(env, fmt.Sprintf("REGISTRY_URL=%s", os.Getenv("REGISTRY_URL"))) @@ -601,11 +601,11 @@ func deployWorker(image string, identifier string, env []string, executionReques } } - log.Printf("envMap", envMap) + // log.Printf("envMap", envMap) clientset, err := getKubernetesClient() if err != nil { fmt.Println("[ERROR]Error getting kubernetes client:", err) - os.Exit(1) + // os.Exit(1) } pod := &corev1.Pod{ From b5f992cb80744ac243c8ffed93fe5064873a523b Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Fri, 13 Oct 2023 13:11:17 +0530 Subject: [PATCH 085/160] removed comments and nodeSelector in docker.go --- backend/go-app/docker.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 6d3c7ddb..5d9eaf73 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -356,10 +356,10 @@ func buildImage(tags []string, dockerfileFolder string) error { } if isKubernetes { - log.Printf("K8S ###################") - log.Print("dockerfileFolder: ", dockerfileFolder) - log.Print("tags: ", tags) - log.Print("only tag: ", tags[1]) + // log.Printf("K8S ###################") + // log.Print("dockerfileFolder: ", dockerfileFolder) + // log.Print("tags: ", tags) + // log.Print("only tag: ", tags[1]) registryName := "" if len(os.Getenv("REGISTRY_URL")) > 0 { @@ -405,9 +405,9 @@ func buildImage(tags []string, dockerfileFolder string) error { }, }, }, - NodeSelector: map[string]string{ - "node": "master", - }, + // NodeSelector: map[string]string{ + // "node": "master", + // }, RestartPolicy: corev1.RestartPolicyNever, Volumes: []corev1.Volume{ { From 04c2ed80a07d5e7a4aa2d8440d57c4f5401e5cc8 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Fri, 13 Oct 2023 14:29:42 +0000 Subject: [PATCH 086/160] fixed issue with app builder --- backend/go-app/docker.go | 142 ++++++++++++---------- functions/kubernetes/shuffle-backend.yaml | 2 +- 2 files changed, 79 insertions(+), 65 deletions(-) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 5d9eaf73..a2dc9fd1 100755 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -379,6 +379,20 @@ func buildImage(tags []string, dockerfileFolder string) error { return err } + BackendPodLabel := "io.kompose.service=backend" + + backendPodList, podListErr := client.CoreV1().Pods("shuffle").List(context.TODO(), metav1.ListOptions{ + LabelSelector: BackendPodLabel, + }) + + if podListErr != nil || len(backendPodList.Items) == 0 { + fmt.Println("Error getting backend pod or no pod found:", podListErr) + return podListErr + } + + backendNodeName := backendPodList.Items[0].Spec.NodeName + log.Printf("[INFO] Backend running on: %s", backendNodeName) + job := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "shuffle-app-builder", @@ -405,9 +419,9 @@ func buildImage(tags []string, dockerfileFolder string) error { }, }, }, - // NodeSelector: map[string]string{ - // "node": "master", - // }, + NodeSelector: map[string]string{ + "node": backendNodeName, + }, RestartPolicy: corev1.RestartPolicyNever, Volumes: []corev1.Volume{ { @@ -463,68 +477,68 @@ func buildImage(tags []string, dockerfileFolder string) error { } } else { - ctx := context.Background() - client, err := client.NewEnvClient() - if err != nil { - log.Printf("Unable to create docker client: %s", err) - return err - } - - log.Printf("[INFO] Docker Tags: %s", tags) - dockerfileSplit := strings.Split(dockerfileFolder, "/") - - // Create a buffer - buf := new(bytes.Buffer) - tw := tar.NewWriter(buf) - defer tw.Close() - baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") - - // Builds the entire folder into buf - err = getParsedTar(tw, baseDir, "") - if err != nil { - log.Printf("Tar issue: %s", err) - } - - dockerFileTarReader := bytes.NewReader(buf.Bytes()) - buildOptions := types.ImageBuildOptions{ - Remove: true, - Tags: tags, - BuildArgs: map[string]*string{}, - } - //NetworkMode: "host", - - httpProxy := os.Getenv("HTTP_PROXY") - if len(httpProxy) > 0 { - buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy - } - httpsProxy := os.Getenv("HTTPS_PROXY") - if len(httpProxy) > 0 { - buildOptions.BuildArgs["https_proxy"] = &httpsProxy - } - - // Build the actual image - imageBuildResponse, err := client.ImageBuild( - ctx, - dockerFileTarReader, - buildOptions, - ) - - if err != nil { - return err - } - - // Read the STDOUT from the build process - defer imageBuildResponse.Body.Close() - buildBuf := new(strings.Builder) - _, err = io.Copy(buildBuf, imageBuildResponse.Body) - if err != nil { - return err - } else { - if strings.Contains(buildBuf.String(), "errorDetail") { - log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) - return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) + ctx := context.Background() + client, err := client.NewEnvClient() + if err != nil { + log.Printf("Unable to create docker client: %s", err) + return err + } + + log.Printf("[INFO] Docker Tags: %s", tags) + dockerfileSplit := strings.Split(dockerfileFolder, "/") + + // Create a buffer + buf := new(bytes.Buffer) + tw := tar.NewWriter(buf) + defer tw.Close() + baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/") + + // Builds the entire folder into buf + err = getParsedTar(tw, baseDir, "") + if err != nil { + log.Printf("Tar issue: %s", err) + } + + dockerFileTarReader := bytes.NewReader(buf.Bytes()) + buildOptions := types.ImageBuildOptions{ + Remove: true, + Tags: tags, + BuildArgs: map[string]*string{}, + } + //NetworkMode: "host", + + httpProxy := os.Getenv("HTTP_PROXY") + if len(httpProxy) > 0 { + buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy + } + httpsProxy := os.Getenv("HTTPS_PROXY") + if len(httpProxy) > 0 { + buildOptions.BuildArgs["https_proxy"] = &httpsProxy + } + + // Build the actual image + imageBuildResponse, err := client.ImageBuild( + ctx, + dockerFileTarReader, + buildOptions, + ) + + if err != nil { + return err + } + + // Read the STDOUT from the build process + defer imageBuildResponse.Body.Close() + buildBuf := new(strings.Builder) + _, err = io.Copy(buildBuf, imageBuildResponse.Body) + if err != nil { + return err + } else { + if strings.Contains(buildBuf.String(), "errorDetail") { + log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) + return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ","))) + } } - } } return nil diff --git a/functions/kubernetes/shuffle-backend.yaml b/functions/kubernetes/shuffle-backend.yaml index 6388b1d9..62c3cdbf 100644 --- a/functions/kubernetes/shuffle-backend.yaml +++ b/functions/kubernetes/shuffle-backend.yaml @@ -390,7 +390,7 @@ spec: key: REGISTRY_AUTH name: env image: dhavald055/shuffle-k8s:shuffle-backend-kubernetes - #imagePullPolicy: Never + imagePullPolicy: Always name: shuffle-backend ports: - containerPort: 5001 From 223e0147a6a20d5cdb973f47cf37c625ea315694 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Fri, 13 Oct 2023 16:51:21 +0000 Subject: [PATCH 087/160] automated opensearch permissions --- functions/kubernetes/shuffle-opensearch.yaml | 7 +++++++ functions/onprem/worker/worker.go | 9 +++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/functions/kubernetes/shuffle-opensearch.yaml b/functions/kubernetes/shuffle-opensearch.yaml index 75ee74da..09a7c03c 100644 --- a/functions/kubernetes/shuffle-opensearch.yaml +++ b/functions/kubernetes/shuffle-opensearch.yaml @@ -64,6 +64,13 @@ spec: # fsGroup: 1000 # GID # nodeSelector: # node: worker1 + initContainers: + - name: volume-permissions + image: busybox + command: ["sh", "-c", "chown -R 1000:1000 /usr/share/opensearch/data"] + volumeMounts: + - name: opensearch-claim0 + mountPath: /usr/share/opensearch/data containers: - env: - name: OPENSEARCH_JAVA_OPTS diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 9221a2fd..57851e8a 100755 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -261,7 +261,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] appName := strings.Join(appDetailsSplit[:len(appDetailsSplit)-1], "_") appVersion := appDetailsSplit[len(appDetailsSplit)-1] - log.Printf("APP VERSION IS: %s", appVersion) + // log.Printf("APP VERSION IS: %s", appVersion) for _, app := range workflowExecution.Workflow.Actions { // log.Printf("[DEBUG] App: %s, Version: %s", appName, appVersion) @@ -294,9 +294,10 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] RestartPolicy: "Never", Containers: []corev1.Container{ { - Name: value, - Image: image, - Env: buildEnvVars(envMap), + Name: value, + Image: image, + Env: buildEnvVars(envMap), + // ImagePullPolicy: corev1.PullAlways, }, }, }, From ca13f3013dc0cd494b56a9cefef201bc13b47bae Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Fri, 13 Oct 2023 16:55:46 +0530 Subject: [PATCH 088/160] removed manual permission process --- functions/kubernetes/kubernetes.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index 890ef51a..40a1b683 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -3,17 +3,7 @@ ### Prerequisites: - Clone the https://github.com/dhaval055/shuffle repository using Git and change the branch to shuffle-k8s then navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. - Running Kubernetes cluster. -- Run the following command for setting up Opensearch and Backend volumes. -- Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. If you're using Minikube for testing, run commands in box below after running ```minikube ssh```. - -``` -cd /mnt -mkdir shuffle-data -cd shuffle-data -mkdir open-search -mkdir backend -sudo chown –R 1000:1000 open-search -``` +- Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. Step 1: Create a namespace called shuffle in a cluster by running ```kubectl create ns shuffle```. From 83b97da868ee6fb0235d107462329079eab3678d Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Mon, 16 Oct 2023 16:11:57 +0530 Subject: [PATCH 089/160] fixed error in docs and added more registry related content --- functions/kubernetes/kubernetes.md | 36 +++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index 40a1b683..cf8a6f03 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -3,26 +3,42 @@ ### Prerequisites: - Clone the https://github.com/dhaval055/shuffle repository using Git and change the branch to shuffle-k8s then navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. - Running Kubernetes cluster. -- Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. +- Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. To setup a docker registry, if you have docker installed on one of your node run following commands. + ``` + openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/reg.key -x509 -days 365 -out certs/reg.crt -subj "/CN=" + docker run -d -p 5000:5000 --restart=always --name shuffle-local-registry \ + -v $(pwd)/certs:/certs \ + -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/reg.crt \ + -e REGISTRY_HTTP_TLS_KEY=/certs/reg.key \ + registry:2 + ``` + - If you've used the above commands to set up a registry, you'll need to skip an SSL verification for your registry. If you're using Containerd as a runtime + add the following lines in /etc/containerd/config.toml + ``` + [plugins."io.containerd.grpc.v1.cri".registry.mirrors.""] + endpoint = ["https://"] + [plugins."io.containerd.grpc.v1.cri".registry.configs."".tls] + insecure_skip_verify = true + + ``` Step 1: Create a namespace called shuffle in a cluster by running ```kubectl create ns shuffle```. -Step 2: Open the shuffle-cm.yaml file and review the configuration values. Change the value of REGISTRY_URL with your registry URL. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap with ‘kubectl apply –f shuffle-cm.yaml –n shuffle’. +Step 2: Open the shuffle-cm.yaml file and review the configuration values. Change the value of REGISTRY_URL with ':5000' where the registry is at. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap with ‘kubectl apply -f shuffle-cm.yaml -n shuffle’. -Step 3: Apply shuffle-role.yaml by using ```kubectl apply –f shuffle-role.yaml –n shuffle```. +Step 3: Apply shuffle-role.yaml by using ```kubectl apply -f shuffle-role.yaml -n shuffle```. -Step 4: Subsequently, establish the role bindings to associate the roles with the appropriate service accounts by applying the shuffle-rolebinding.yaml configuration using ```kubectl apply –f shuffle-rolebinding.yaml –n shuffle```. +Step 4: Subsequently, establish the role bindings to associate the roles with the appropriate service accounts by applying the shuffle-rolebinding.yaml configuration using ```kubectl apply -f shuffle-rolebinding.yaml -n shuffle```. -Step 5: Create storage-class for shuffle by running ```kubectl apply –f shuffle-sc.yaml –n shuffle```. +Step 5: Create storage-class for shuffle by running ```kubectl apply -f shuffle-sc.yaml -n shuffle```. -Step 6: Apply the shuffle-opensearch.yaml configuration to create a Persistent Volume Claim (PVC) for storage, a Deployment to manage the OpenSearch pod, and a Service to expose OpenSearch within the cluster by using ```kubectl apply –f shuffle-opensearch.yaml –n shuffle```. +Step 6: Apply the shuffle-opensearch.yaml configuration to create a Persistent Volume Claim (PVC) for storage, a Deployment to manage the OpenSearch pod, and a Service to expose OpenSearch within the cluster by using ```kubectl apply -f shuffle-opensearch.yaml -n shuffle```. -Step 7: Now deploy the backend using ```kubectl apply –f shuffle-backend.yaml –n shuffle```. +Step 7: Now deploy the backend using ```kubectl apply -f shuffle-backend.yaml -n shuffle```. -Step 8: Ensure that the Shuffle Backend is successfully deployed and running before proceeding with the frontend deployment, as the frontend may depend on services provided by the backend. ```kubectl apply –f shuffle-frontend.yaml –n shuffle```. +Step 8: Ensure that the Shuffle Backend is successfully deployed and running before proceeding with the frontend deployment, as the frontend may depend on services provided by the backend. ```kubectl apply -f shuffle-frontend.yaml -n shuffle```. -Step 9: Run ```kubectl apply -f shuffle-orborus.yaml```. This final step ensures that Shuffle Orborus is deployed, enabling the workflow executions in your Kubernetes-based Shuffle setup. +Step 9: Run ```kubectl apply -f shuffle-orborus.yaml -n shuffle```. This final step ensures that Shuffle Orborus is deployed, enabling the workflow executions in your Kubernetes-based Shuffle setup. Now, open ```https://:30008``` or ```http://:30007```. You should be seeing a signup page. NODE_IP should be where the frontend is deployed. - From 3575ac88faed7acfe22b48cbe394f2c90cc7c9d9 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 17 Oct 2023 02:39:33 +0200 Subject: [PATCH 090/160] Fixed more cloudsync pieces --- backend/go-app/main.go | 74 +++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 8a4de4de..ad8c63b4 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -84,7 +84,6 @@ var registryName = "registry.hub.docker.com" var runningEnvironment = "onprem" var syncUrl = "https://shuffler.io" -var syncSubUrl = "https://shuffler.io" var dbclient *datastore.Client @@ -1102,7 +1101,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { userOrgs = shuffle.SortOrgList(userOrgs) orgPriorities := org.Priorities if len(org.Priorities) < 10 { - log.Printf("[WARNING] Should find and add priorities as length is less than 10 for org %s", userInfo.ActiveOrg.Id) + //log.Printf("[WARNING] Should find and add priorities as length is less than 10 for org %s", userInfo.ActiveOrg.Id) newPriorities, err := shuffle.GetPriorities(ctx, userInfo, org) if err != nil { log.Printf("[WARNING] Failed getting new priorities for org %s: %s", org.Id, err) @@ -3748,16 +3747,69 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error { return nil } + func remoteOrgJobHandler(org shuffle.Org, interval int) error { + + // Check if it's 1 in 10 (10% chance random) + backupJob := shuffle.BackupJob{} + + // Check if workflow backup is active + // Check if app backup is active + ctx := context.Background() + + foundUser := org.Users[0] + for _, user := range org.Users { + if user.Role == "admin" { + foundUser = user + break + } + } + + if org.SyncConfig.WorkflowBackup { + workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser) + if err != nil { + log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err) + } else { + backupJob.Workflows = workflows + } + } + + if org.SyncConfig.AppBackup && len(org.Users) > 0 { + + apps, err := shuffle.GetPrioritizedApps(ctx, foundUser) + if err != nil { + log.Printf("[ERROR] Failed getting backup apps for org %s: %s", org.Id, err) + } else { + backupJob.Apps = apps + } + } + + info, err := shuffle.GetOrgStatistics(ctx, org.Id) + if err != nil { + log.Printf("[ERROR] Failed getting org statistics backup for org %s: %s", org.Id, err) + } else { + backupJob.Stats = *info + } + + backupJobData, err := json.Marshal(backupJob) + if err != nil { + log.Printf("[ERROR] Failed marshalling backup job: %s", err) + backupJobData = []byte{} + } + + client := &http.Client{} syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl) req, err := http.NewRequest( - "GET", + "POST", syncUrl, - nil, + bytes.NewBuffer(backupJobData), ) req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey)) + + //log.Printf("[INFO] Sending org sync with autho %s", org.SyncConfig.Apikey) + newresp, err := client.Do(req) if err != nil { //log.Printf("Failed request in org sync: %s", err) @@ -4758,7 +4810,7 @@ func runInit(ctx context.Context) { job := func() { err := remoteOrgJobHandler(org, interval) if err != nil { - log.Printf("[ERROR] Failed request with remote org setup (2): %s", err) + log.Printf("[ERROR] Failed request with remote org setup (3): %s", err) } } @@ -5242,7 +5294,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { b, err := json.Marshal(requestData) if err != nil { - log.Printf("Failed marshaling api key data: %s", err) + log.Printf("[ERROR] Failed marshaling api key data: %s", err) resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed cloud sync: %s"}`, err))) return @@ -5269,7 +5321,8 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { return } - //log.Printf("Respbody: %s", string(respBody)) + log.Printf("[DEBUG] Respbody from sync: %s", string(respBody)) + responseData := retStruct{} err = json.Unmarshal(respBody, &responseData) if err != nil { @@ -5967,10 +6020,6 @@ func initHandlers() { r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", shuffle.SetAuthenticationConfig).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/authentication/{appauthId}", shuffle.DeleteAppAuthentication).Methods("DELETE", "OPTIONS") - // Related to NFT things - r.HandleFunc("/api/v1/workflows/collections/load", shuffle.LoadCollections).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/workflows/collections/{key}", shuffle.HandleGetCollection).Methods("GET", "OPTIONS") - // Related to use-cases that are not directly workflows. r.HandleFunc("/api/v1/workflows/usecases/{key}", shuffle.HandleGetUsecase).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/workflows/usecases", shuffle.LoadUsecases).Methods("GET", "OPTIONS") @@ -6000,6 +6049,9 @@ func initHandlers() { r.HandleFunc("/api/v1/workflows/{key}", shuffle.GetSpecificWorkflow).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/workflows/recommend", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS") + // First v2 API + r.HandleFunc("/api/v2/workflows/{key}/executions", shuffle.GetWorkflowExecutionsV2).Methods("GET", "OPTIONS") + // New for recommendations in Shuffle r.HandleFunc("/api/v1/recommendations/get_actions", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/recommendations/modify", shuffle.HandleRecommendationAction).Methods("POST", "OPTIONS") From 0579f60b58e5c03aa5fe563cb331ebbb1c3d1bb5 Mon Sep 17 00:00:00 2001 From: Frikky Date: Tue, 17 Oct 2023 02:39:45 +0200 Subject: [PATCH 091/160] Added user check on usecase page --- frontend/src/App.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 2180be03..f2f7a06a 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -326,6 +326,7 @@ const App = (message, props) => { path="/usecases" element={ Date: Tue, 17 Oct 2023 02:40:32 +0200 Subject: [PATCH 092/160] Added statistics to both backend and frontend + cloud sync --- .env | 4 +- backend/go-app/go.mod | 2 +- backend/go-app/walkoff.go | 58 - frontend/src/components/AppFramework.jsx | 2 - frontend/src/components/Appsearch.jsx | 2 +- .../src/components/AuthenticationItem.jsx | 1 - .../src/components/AuthenticationWindow.jsx | 7 - frontend/src/components/Billing.jsx | 1326 +++++++++++---- frontend/src/components/BillingStats.jsx | 280 ++++ frontend/src/components/ConfigureWorkflow.jsx | 877 ++++++---- frontend/src/components/EditWorkflow.jsx | 45 +- frontend/src/components/ExploreWorkflow.jsx | 324 ++++ frontend/src/components/Header.jsx | 7 +- frontend/src/components/ParsedAction.jsx | 48 +- frontend/src/components/Priority.jsx | 2 +- frontend/src/components/ShuffleCodeEditor.jsx | 42 +- frontend/src/components/WelcomeForm2.jsx | 1486 ++++++++++------- frontend/src/components/WorkflowGrid.jsx | 7 +- frontend/src/components/WorkflowPaper.jsx | 2 +- .../src/components/WorkflowTemplatePopup.jsx | 382 +++++ frontend/src/theme.jsx | 9 +- frontend/src/views/Admin.jsx | 30 +- frontend/src/views/AngularWorkflow.jsx | 58 +- frontend/src/views/Apps.jsx | 3 +- frontend/src/views/Dashboard.jsx | 302 ++-- frontend/src/views/LoginPage.jsx | 19 +- frontend/src/views/UpdateAuthentication.jsx | 34 +- frontend/src/views/Welcome.jsx | 84 +- frontend/src/views/Workflows.jsx | 264 ++- functions/onprem/worker/build.sh | 16 +- functions/onprem/worker/go.mod | 12 +- functions/onprem/worker/go.sum | 53 + 32 files changed, 4035 insertions(+), 1753 deletions(-) create mode 100644 frontend/src/components/BillingStats.jsx create mode 100644 frontend/src/components/ExploreWorkflow.jsx create mode 100644 frontend/src/components/WorkflowTemplatePopup.jsx diff --git a/.env b/.env index 07deaf4a..b0a6a6cc 100755 --- a/.env +++ b/.env @@ -71,8 +71,8 @@ SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500 # 1500 by default # the container to the docker0 SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE=eth0 -# Used for auto-cleanup of containers. REALLY important at scale. -SHUFFLE_CONTAINER_AUTO_CLEANUP=false +# Used for auto-cleanup of containers. REALLY important at scale. Set to false to see all container info. +SHUFFLE_CONTAINER_AUTO_CLEANUP=true SHUFFLE_ELASTIC=true SHUFFLE_LOGS_DISABLED=false SHUFFLE_CHAT_DISABLED=false # Controls support chat diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index d6e77d3c..b44a20e2 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -//replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared go 1.19 diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index d98f922b..74676a45 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -700,64 +700,6 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { } } - /* - // Removed as UserInput is now handled as an app - if actionResult.Status == "WAITING" && actionResult.Action.AppName == "User Input" { - log.Printf("[INFO] SHOULD WAIT A BIT AND RUN USER INPUT! WAITING!") - - var trigger shuffle.Trigger - err = json.Unmarshal([]byte(actionResult.Result), &trigger) - if err != nil { - log.Printf("[WARNING] Failed unmarshaling actionresult for user input: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - orgId := workflowExecution.ExecutionOrg - if len(workflowExecution.OrgId) == 0 && len(workflowExecution.Workflow.OrgId) > 0 { - orgId = workflowExecution.Workflow.OrgId - } - - err := handleUserInput(trigger, orgId, workflowExecution.Workflow.ID, workflowExecution.ExecutionId) - if err != nil { - log.Printf("[WARNING] Failed userinput handler: %s", err) - - actionResult.Result = fmt.Sprintf(`{"success": false, "reason": "%s"}`, err) - - workflowExecution.Results = append(workflowExecution.Results, actionResult) - workflowExecution.Status = "ABORTED" - err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true) - if err != nil { - log.Printf("[WARNING] Failed to set execution during wait: %s", err) - } else { - log.Printf("[INFO] Successfully set the execution %s to waiting.", workflowExecution.ExecutionId) - } - - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Error: %s"}`, err))) - return - } else { - log.Printf("[INFO] Successful userinput handler") - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "CLOUD IS DONE"}`))) - - actionResult.Result = `{"success": True, "reason": "Waiting for user feedback based on configuration"}` - - workflowExecution.Results = append(workflowExecution.Results, actionResult) - workflowExecution.Status = actionResult.Status - err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true) - if err != nil { - log.Printf("[WARNING] Failed setting userinput: %s", err) - } else { - log.Printf("[DEBUG] Successfully set the execution to waiting.") - } - } - - return - } - */ - runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp) } diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index aca62d61..9750ff7f 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -1856,8 +1856,6 @@ const AppFramework = (props) => { //autounselectify={true} var usecasediff = -100 const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color - console.log("Background: ", bgColor) - return (
diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index 7408dad5..87851541 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -200,7 +200,7 @@ const Appsearch = props => { const CustomHits = connectHits(InputHits) return ( -
+
{/* showSearch === false ? null :
diff --git a/frontend/src/components/AuthenticationItem.jsx b/frontend/src/components/AuthenticationItem.jsx index 593eacb4..eae08e69 100644 --- a/frontend/src/components/AuthenticationItem.jsx +++ b/frontend/src/components/AuthenticationItem.jsx @@ -18,7 +18,6 @@ import { Grid, Paper, Typography, - TextField, Zoom, } from "@mui/material"; diff --git a/frontend/src/components/AuthenticationWindow.jsx b/frontend/src/components/AuthenticationWindow.jsx index d7bcc55a..7d32f597 100755 --- a/frontend/src/components/AuthenticationWindow.jsx +++ b/frontend/src/components/AuthenticationWindow.jsx @@ -294,9 +294,6 @@ const AuthenticationData = (props) => { InputProps={{ style: { color: "white", - marginLeft: "5px", - maxWidth: "95%", - height: 50, fontSize: "1em", }, disableUnderline: true, @@ -386,7 +383,6 @@ const AuthenticationData = (props) => { PaperProps={{ style: { pointerEvents: "auto", - backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: 600, minHeight: 600, @@ -441,9 +437,6 @@ const AuthenticationData = (props) => { InputProps={{ style: { color: "white", - marginLeft: "5px", - maxWidth: "95%", - height: 50, fontSize: "1em", }, }} diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 71d01789..0b9c484d 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -1,316 +1,1010 @@ -import React, { useState, useEffect } from "react"; -import theme from "../theme.jsx"; -import ReactGA from 'react-ga4'; - -import { - Paper, - Typography, - Divider, - Button, - Grid, - Card, -} from "@mui/material"; - -import { useAlert } from "react-alert"; -import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; - -const Billing = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; - console.log("Billing: ", billingInfo); - const alert = useAlert(); - - const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : "" - console.log("Stripe: ", stripe) - - const paperStyle = { - padding: 20, - height: "100%", - width: "100%", - backgroundColor: theme.palette.surfaceColor, - border: "1px solid rgba(255,255,255,0.3)", - marginRight: 10, - } - - const isCloud = - window.location.host === "localhost:3002" || - window.location.host === "shuffler.io"; - - billingInfo.subscription = { - "active": true, - "name": "Pay as you go", - "price": typecost_single, - "currency": "USD", - "currency_text": "$", - "interval": "app run / month", - "description": "Pay as you go", - "features": [ - "Includes 10.000 app run/month for free. ", - "Pay for what you use with no minimum commitment and cancel anytime.", - ], - "limit": 10000, - } - - - const handleStripeRedirect = () => { - //var priceItem = "price_1MRNF1DzMUgUjxHSfFTUb2Xh" - if (stripe == "") { - console.log("Stripe not loaded") - return - } - - 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` - var checkoutObject = { - lineItems: [ - { - price: priceItem, - quantity: 1 - }, - ], - mode: "subscription", - billingAddressCollection: "auto", - successUrl: successUrl, - cancelUrl: failUrl, - clientReferenceId: props.userdata.active_org.id, - } - //submitType: "donate", - - stripe.redirectToCheckout(checkoutObject) - .then(function (result) { - console.log("SUCCESS STRIPE?: ", result) - - ReactGA.event({ - category: "pricing", - action: "add_card_success", - label: "", - }) - }) - .catch(function(error) { - console.error("STRIPE ERROR: ", error) - - ReactGA.event({ - category: "pricing", - action: "add_card_error", - label: "", - }) - }); - } - - const cancelSubscriptions = (subscription_id) => { - const orgId = selectedOrganization.id; - const data = { - subscription_id: subscription_id, - action: "cancel", - org_id: selectedOrganization.id, - }; - - const url = globalUrl + `/api/v1/orgs/${orgId}/cancel`; - 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"); - } - - if (handleGetOrg != undefined) { - 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 SubscriptionObject = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, } = props; - - console.log("Sub: ", subscription) - var top_text = "Base Access" - if (subscription.limit === undefined && subscription.level !== undefined) { - - subscription.name = "Enterprise" - subscription.currency_text = "$" - subscription.price = subscription.level*180 - subscription.limit = subscription.level*100000 - subscription.interval = subscription.recurrence - subscription.features = [ - "Includes " + subscription.limit + " app runs/month. ", - "Multi-Tenancy and Region-Selection", - "And all other features from /pricing", - ] - } - - if (subscription.name === "Enterprise" && subscription.active === true) { - top_text = "Current Plan" - } - - return ( - -
- - {top_text} - -
- -
- - {subscription.name} - -
- - {subscription.currency_text}{subscription.price} - - - / {subscription.interval} - -
- - Features - -
    - {subscription.features !== undefined && subscription.features !== null ? - subscription.features.map((feature, index) => { - return ( -
  • - - {feature} - -
  • - ) - }) - : null} -
-
- {/*subscription.name === "Pay as you go" && subscription.limit <= 10000 ? - - - You are not subscribed to any plan and are using the free plan with max 10,000 apps per month. Activate billing to de-activate this limit. - - - - : null*/} -
- ) - } - - - return ( -
- - Billing - - - We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below. - -
- {billingInfo.subscription !== undefined && billingInfo.subscription !== null ? - - : null} - {isCloud && - selectedOrganization.subscriptions !== undefined && - selectedOrganization.subscriptions !== null && - selectedOrganization.subscriptions.length > 0 ? - - selectedOrganization.subscriptions - .reverse() - .map((sub, index) => { - return ( - - ) - }) - : null} - {/* - - - Quantity: {sub.level} -
- Recurrence: {sub.recurrence} -
- {sub.active ? ( -
- Started:{" "} - {new Date(sub.startdate * 1000).toISOString()} -
- -
- ) : ( -
- Cancelled:{" "} - {new Date( - sub.cancellationdate * 1000 - ).toISOString()} -
- - Status: Deactivated - -
- )} - - - */} -
-
- ) -} - -export default Billing; +import React, { useState, useEffect } from "react"; +import ReactGA from 'react-ga4'; + +import theme from "../theme.jsx"; +import { useTheme } from "@mui/styles"; +import countries from "../components/Countries.jsx"; +import { + Box, + Paper, + Typography, + Divider, + Button, + Grid, + Card, + List, + ListItemText, + ListItem, + Dialog, + DialogTitle, + DialogContent, + TextField, +} from "@mui/material"; + +import { useNavigate, Link } from "react-router-dom"; +import { Autocomplete } from "@mui/material"; +import { toast } from "react-toastify" + +import { + Cached as CachedIcon, +} from "@mui/icons-material"; + +//import { useAlert +import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; +import BillingStats from "../components/BillingStats.jsx"; + +const Billing = (props) => { + const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; + //const alert = useAlert(); + let navigate = useNavigate(); + + const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); + const [dealList, setDealList] = React.useState([]); + 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 stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : "" + const products = [ + { code: "", label: "MSSP", phone: "" }, + { code: "", label: "Enterprise", phone: "" }, + { code: "", label: "Consultancy", phone: "" }, + { code: "", label: "Support", phone: "" }, + ]; + + const handleGetDeals = (orgId) => { + console.log("Get deals!"); + + if (orgId.length === 0) { + toast( + "Organization ID not defined (get deals). 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) { + toast("Failed loading deals. Contact support if this persists"); + } else { + setDealList(responseJson); + } + }) + .catch((error) => { + console.log("Error getting org deals: ", error); + toast( + "Failed getting deals for your org. Contact support if this persists." + ); + }); + }; + + useEffect(() => { + if (isCloud && selectedOrganization.partner_info !== undefined && selectedOrganization.partner_info.reseller === true) { + handleGetDeals(selectedOrganization.id); + } + }, []) + + const paperStyle = { + padding: 20, + height: "100%", + minHeight: 280, + maxWidth: 400, + width: "100%", + backgroundColor: theme.palette.surfaceColor, + borderRadius: theme.palette.borderRadius, + border: "1px solid rgba(255,255,255,0.3)", + marginRight: 10, + } + + const isCloud = + window.location.host === "localhost:3002" || + window.location.host === "shuffler.io"; + + billingInfo.subscription = { + "active": true, + "name": "Pay as you go", + "price": typecost_single, + "currency": "USD", + "currency_text": "$", + "interval": "app run / month", + "description": "Pay as you go", + "features": [ + "Includes 10.000 app run/month for free. ", + "Pay for what you use with no minimum commitment and cancel anytime.", + ], + "limit": 10000, + } + + + const handleStripeRedirect = () => { + //var priceItem = "price_1MRNF1DzMUgUjxHSfFTUb2Xh" + if (stripe == "") { + console.log("Stripe not loaded") + return + } + + 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` + var checkoutObject = { + lineItems: [ + { + price: priceItem, + quantity: 1 + }, + ], + mode: "subscription", + billingAddressCollection: "auto", + successUrl: successUrl, + cancelUrl: failUrl, + clientReferenceId: props.userdata.active_org.id, + } + //submitType: "donate", + + stripe.redirectToCheckout(checkoutObject) + .then(function (result) { + console.log("SUCCESS STRIPE?: ", result) + + ReactGA.event({ + category: "pricing", + action: "add_card_success", + label: "", + }) + }) + .catch(function(error) { + console.error("STRIPE ERROR: ", error) + + ReactGA.event({ + category: "pricing", + action: "add_card_error", + label: "", + }) + }); + } + + const cancelSubscriptions = (subscription_id) => { + const orgId = selectedOrganization.id; + const data = { + subscription_id: subscription_id, + action: "cancel", + org_id: selectedOrganization.id, + }; + + const url = globalUrl + `/api/v1/orgs/${orgId}/cancel`; + 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"); + } + + if (handleGetOrg != undefined) { + handleGetOrg(selectedOrganization.id); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success !== undefined && responseJson.success) { + toast("Successfully stopped subscription!"); + } else { + toast("Failed stopping subscription. Please contact us."); + } + }) + .catch(function (error) { + console.log("Error: ", error); + toast("Failed stopping subscription. Please contact us."); + }); + }; + + const SubscriptionObject = (props) => { + const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props; + + var top_text = "Base Access" + if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) { + subscription.name = "Enterprise" + subscription.currency_text = "$" + subscription.price = subscription.level*180 + subscription.limit = subscription.level*100000 + subscription.interval = subscription.recurrence + subscription.features = [ + "Includes " + subscription.limit + " app runs/month. ", + "Multi-Tenancy and Region-Selection", + "And all other features from /pricing", + ] + } + + var newPaperstyle = JSON.parse(JSON.stringify(paperStyle)) + if (subscription.name === "Enterprise" && subscription.active === true) { + top_text = "Current Plan" + + newPaperstyle.border = "1px solid #f85a3e" + } + + var showSupport = false + if (subscription.name.includes("default")) { + top_text = "Custom Contract" + newPaperstyle.border = "1px solid #f85a3e" + showSupport = true + } + + if (subscription.name.includes("App Run Units")) { + top_text = "Cloud Access" + showSupport = true + } + + if (subscription.name.includes("Open Source")) { + top_text = "Open Source" + showSupport = true + } + + if (subscription.name.includes("Scale")) { + top_text = "Scale access" + } + + if (highlight === true) { + // Add an "Upgrade now" button + newPaperstyle.border = "1px solid #f85a3e" + } + + return ( + +
+ + {top_text} + +
+ +
+ + {subscription.name} + + + {subscription.currency_text !== undefined ? +
+ + {subscription.currency_text}{subscription.price} + + + / {subscription.interval} + +
+ : null} + + + Features + +
    + {subscription.features !== undefined && subscription.features !== null ? + subscription.features.map((feature, index) => { + var parsedFeature = feature + if (feature.includes("Documentation: ")) { + parsedFeature = + + Documentation to get started + + } + + if (feature.includes("Licensed Worker: ")) { + parsedFeature = + + Download the licensed worker + + } + + return ( +
  • + + {parsedFeature} + +
  • + ) + }) + : null} +
+
+ {(highlight === true && subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name.includes("Scale") ? + + + {subscription.name.includes("Scale") ? + "" + : + "You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit." + } + + + + : null} + {showSupport ? + + : null } +
+ ) + } + + 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} +
+ + +
+
+
+ ); + + 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); + toast( + "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()); + toast("Failed adding deal reg: ", error); + }); + }; + + return ( +
+ {addDealModal} + + Billing + + + {isCloud ? + "We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below." + : + "Shuffle is an Open Source automation platform, and no license is required to use it. You may however activate Cloud Sync, get our Scale license, get help with Kubernetes, or talk to Shuffle's Support team to get automation help." + } + +
+ {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? + + : !isCloud ? + + + + + : null} + {isCloud && + selectedOrganization.subscriptions !== undefined && + selectedOrganization.subscriptions !== null && + selectedOrganization.subscriptions.length > 0 ? + + selectedOrganization.subscriptions + .reverse() + .map((sub, index) => { + return ( + + ) + }) + : null} + {/* + + + Quantity: {sub.level} +
+ Recurrence: {sub.recurrence} +
+ {sub.active ? ( +
+ Started:{" "} + {new Date(sub.startdate * 1000).toISOString()} +
+ +
+ ) : ( +
+ Cancelled:{" "} + {new Date( + sub.cancellationdate * 1000 + ).toISOString()} +
+ + Status: Deactivated + +
+ )} + + + */} +
+ {isCloud && + selectedOrganization.partner_info !== undefined && + selectedOrganization.partner_info.reseller === true ? ( +
+ + Reseller dashboard + + + + + + + + + + + + + + + + + + {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} +
+ + Billing Usage Overview + +
+ +
+ ) +} + +export default Billing; diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx new file mode 100644 index 00000000..3f7a7516 --- /dev/null +++ b/frontend/src/components/BillingStats.jsx @@ -0,0 +1,280 @@ +import React, { useState, useEffect } from 'react'; + +import classNames from "classnames"; +import theme from '../theme.jsx'; + +import { + Tooltip, + TextField, + IconButton, + Button, + Typography, + Grid, + Paper, + Chip, + Checkbox, +} from "@mui/material"; + +import { + BarChart, + RadialBarChart, + RadialAreaChart, + RadialAxis, + StackedBarSeries, + TooltipArea, + ChartTooltip, + TooltipTemplate, + RadialAreaSeries, + RadialPointSeries, + RadialArea, + RadialLine, + TreeMap, + TreeMapSeries, + TreeMapLabel, + TreeMapRect, + Line, + LineChart, + LineSeries, + LinearYAxis, + LinearXAxis, + LinearYAxisTickSeries, + LinearXAxisTickSeries, + Area, + AreaChart, + AreaSeries, + AreaSparklineChart, + PointSeries, + GridlineSeries, + Gridline, + Stripes, + Gradient, + GradientStop, + LinearXAxisTickLabel, +} from 'reaviz'; + +const LineChartWrapper = ({keys, inputname, height, width}) => { + const [hovered, setHovered] = useState(""); + const inputdata = keys.data === undefined ? keys : keys.data + + return ( +
+ + {inputname} + + } /> + } + /> +
+ ) +} + + +const AppStats = (defaultprops) => { + const { globalUrl, selectedOrganization, userdata, } = defaultprops; + const [keys, setKeys] = useState([]) + const [searches, setSearches] = useState([]); + const [clickData, setClickData] = useState(undefined); + const [conversionData, setConversionData] = useState(undefined); + const [statistics, setStatistics] = useState(undefined); + const [appRuns, setAppruns] = useState(undefined); + const [workflowRuns, setWorkflowRuns] = useState(undefined); + const [subflowRuns, setSubflowRuns] = useState(undefined); + + const handleDataSetting = (inputdata, grouping) => { + if (inputdata === undefined || inputdata === null) { + return + } + + const dailyStats = inputdata.daily_statistics + if (dailyStats === undefined || dailyStats === null) { + return + } + + console.log("Looking at daily data: ", inputdata) + + var appRuns = { + "key": "App Runs", + "data": [] + } + + var workflowRuns = { + "key": "Workflow Runs (includes subflows)", + "data": [] + } + + var subflowRuns = { + "key": "Subflow Runs", + "data": [] + } + + for (let key in dailyStats) { + // Always skips first one as it has accumulated data in it + if (key === 0) { + continue + } + + const item = dailyStats[key] + + if (item["date"] === undefined) { + console.log("No date: ", item) + continue + } + + // Check if app_executions key in item + if (item["app_executions"] !== undefined && item["app_executions"] !== null) { + appRuns["data"].push({ + key: new Date(item["date"]), + data: item["app_executions"] + }) + } + + // Check if workflow_executions key in item + if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) { + workflowRuns["data"].push({ + key: new Date(item["date"]), + data: item["workflow_executions"] + }) + } + + if (item["subflow_executions"] !== undefined && item["subflow_executions"] !== null) { + subflowRuns["data"].push({ + key: new Date(item["date"]), + data: item["subflow_executions"] + }) + } + } + + // Adds data for today + console.log("Inputdata: ", inputdata) + if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) { + appRuns["data"].push({ + key: new Date(), + data: inputdata["daily_app_executions"] + }) + } + + if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) { + workflowRuns["data"].push({ + key: new Date(), + data: inputdata["daily_workflow_executions"] + }) + } + + if (inputdata["daily_subflow_executions"] !== undefined && inputdata["daily_subflow_executions"] !== null) { + subflowRuns["data"].push({ + key: new Date(), + data: inputdata["daily_subflow_executions"] + }) + } + + setSubflowRuns(subflowRuns) + setWorkflowRuns(workflowRuns) + setAppruns(appRuns) + } + + const getStats = () => { + fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for workflows :O!: ", response.status); + return; + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson["success"] === false) { + return + } + + setStatistics(responseJson) + handleDataSetting(responseJson, "day") + }) + .catch((error) => { + console.log("error: ", error) + }); + } + + useEffect(() => { + getStats() + }, []) + + const paperStyle = { + textAlign: "center", + padding: 40, + margin: 5, + backgroundColor: theme.palette.surfaceColor, + maxWidth: 300, + } + + const data = ( +
+ + All Stat widgets are monthly and gathered from Your Organization Statistics. + This is a feature to help give you more insight into Shuffle, and will be populating over time. + + {statistics !== undefined ? +
+ + + {statistics.monthly_workflow_executions} + + + Workflow Runs + + + + + {statistics.monthly_app_executions} + + + App Runs + + +
+ : null} + + {appRuns === undefined ? + null + : + + } + + {workflowRuns === undefined ? + null + : + + } + + {subflowRuns === undefined ? + null + : + + } +
+ ) + + const dataWrapper = ( +
{data}
+ ); + + return dataWrapper; +} + +export default AppStats; diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 0e80540c..52a3ac1b 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -16,11 +16,17 @@ import { ListItem, ListItemText, Collapse, + IconButton, } from "@mui/material"; + import { FavoriteBorder as FavoriteBorderIcon, Error as ErrorIcon, CheckCircleRounded as CheckCircleRoundedIcon, + ExpandMore as ExpandMoreIcon, + ExpandLess as ExpandLessIcon, + Check as CheckIcon, + Visibility as VisibilityIcon, } from "@mui/icons-material"; import { FixName } from "../views/Apps.jsx"; import aa from 'search-insights' @@ -34,27 +40,26 @@ import aa from 'search-insights' // Specifically used for UNSAVED workflows only? const ConfigureWorkflow = (props) => { const { - userdata, - globalUrl, + apps, theme, + isCloud, workflow, + userdata, + globalUrl, + newWebhook, + referenceUrl, + saveWorkflow, + showTriggers, + submitSchedule, + setSelectedApp, + selectedAction, appAuthentication, setSelectedAction, - setAuthenticationModalOpen, - setSelectedApp, - apps, - selectedAction, - setConfigureWorkflowModalOpen, - saveWorkflow, - newWebhook, - submitSchedule, - referenceUrl, - isCloud, + workflowExecutions, + getWorkflowExecution, setAuthenticationType, - alert, - showTriggers, - workflowExecutions, - getWorkflowExecution, + setAuthenticationModalOpen, + setConfigureWorkflowModalOpen, } = props; const [requiredActions, setRequiredActions] = React.useState([]); @@ -64,9 +69,39 @@ const ConfigureWorkflow = (props) => { const [itemChanged, setItemChanged] = React.useState(false); const [firstLoad, setFirstLoad] = React.useState(""); const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false); + const [loopRunning, setLoopRunning] = useState(false) - const [checkStarted, setCheckStarted] = React.useState(false); + const [checkStarted, setCheckStarted] = React.useState(false); + const stop = () => { + setLoopRunning(false) + } + + const start = () => { + setLoopRunning(true) + } + + useEffect(() => { + if (loopRunning) { + const intervalId = setInterval(() => { + if (!loopRunning) { + clearInterval(intervalId); + } + + + if (getWorkflowExecution !== undefined && workflowExecutions !== undefined) { + const paramkey = workflow.id + getWorkflowExecution(paramkey) + } else { + console.log("Executions or getWorkflowExecutions not defined") + } + }, 3000) + + return () => clearInterval(intervalId); + } + }, [loopRunning]) + + /* const { start, stop } = useInterval({ duration: 3000, startImmediate: false, @@ -79,6 +114,7 @@ const ConfigureWorkflow = (props) => { } }, }); + */ // ONLY when component is being unloaded, run stop() function // This is to prevent the interval from running when the component is not being used @@ -92,16 +128,18 @@ const ConfigureWorkflow = (props) => { */ // Where is this from? - if (workflow === undefined || workflow === null) { + if (workflow === undefined || workflow === null || workflow.id === undefined) { return null; } if (apps === undefined || apps === null) { - return null; + console.log("Apps is undefined or null: ", apps) + return null; } if (appAuthentication === undefined || appAuthentication === null) { - return null; + console.log("App authentication is undefined or null: ", appAuthentication) + return null; } const getApp = (actionId, appId) => { @@ -136,13 +174,19 @@ const ConfigureWorkflow = (props) => { if (firstLoad.length === 0 || firstLoad !== workflow.id) { if (apps === undefined || apps === null || apps.length === 0) { console.log("No apps loaded: ", apps); - setConfigureWorkflowModalOpen(false); + + if (setConfigureWorkflowModalOpen !== undefined) { + setConfigureWorkflowModalOpen(false); + } + return null; } setFirstLoad(workflow.id) + const newactions = []; for (let [key, keyval] in Object.entries(workflow.actions)) { + const action = workflow.actions[key]; var newaction = { large_image: action.large_image, @@ -154,34 +198,36 @@ const ConfigureWorkflow = (props) => { auth_done: false, action_ids: [], action: action, - update_version: action.app_version, + update_version: action.app_version, app: {}, - steps: [], - show_steps: false, + steps: [], + show_steps: false, } - //console.log("Action: ", key, keyval) + if (action.app_name.toLowerCase().endsWith("_api")) { + action.app_name = action.app_name.slice(0, -4) + } - const app = apps.find((app) => - app.id === action.app_id || - (app.name === action.app_name && - (app.app_version === action.app_version || (app.loop_versions !== null && app.loop_versions.includes(action.app_version)))) - ) - - //console.log("FOUND APP: ", app) + // ID match OR name match + version match + //const app = apps.find((app) => app.id === action.app_id || (app.name === action.app_name && (app.app_version === action.app_version || (app.loop_versions !== null && app.loop_versions.includes(action.app_version))))) + // + // without version match + const newappname = action.app_name.toLowerCase().replaceAll(" ", "_") + const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname) if (app === undefined || app === null) { + const subapp = apps.find(app => app.name === action.app_name) - if (subapp !== undefined && subapp !== null) { - newaction.update_version = "1.1.0" - } + if (subapp !== undefined && subapp !== null) { + newaction.update_version = "1.1.0" + } newaction.must_activate = true; - newaction.steps.push({ - "title": "Activate app", - "type": "activate", - "required": true, - }) + newaction.steps.push({ + "title": "Activate app", + "type": "activate", + "required": true, + }) } else { if (action.authentication_id === "" && app.authentication.required === true && action.parameters !== undefined && action.parameters !== null) { // Check if configuration is filled or not @@ -199,20 +245,19 @@ const ConfigureWorkflow = (props) => { } } - newaction.steps.push({ - "title": "Authenticate app", - "type": "authenticate", - "required": true, - }) + newaction.steps.push({ + "title": "Authenticate app", + "type": "authenticate", + "required": true, + }) if (!filled) { newaction.must_authenticate = true; newaction.action_ids.push(action.id); } } else if (action.authentication_id !== "" && app.authentication.required === true) { - console.log("Should verify authentication ID ", action.authentication_id) - - } + console.log("Should verify authentication ID ", action.authentication_id) + } newaction.app = app; } @@ -276,80 +321,78 @@ const ConfigureWorkflow = (props) => { } if (workflow.workflow_variables !== undefined && workflow.workflow_variables !== null && workflow.workflow_variables.length !== 0) { - for (let [key,keyval] in Object.entries(workflow.workflow_variables)) { - const variable = workflow.workflow_variables[key]; - if (variable.value === undefined || variable.value === undefined || variable.value.length === 0) { - variable.value = ""; - requiredVariables.push(variable); - } + for (let [key,keyval] in Object.entries(workflow.workflow_variables)) { + const variable = workflow.workflow_variables[key]; - variable.index = key; - } + if (variable.value === undefined || variable.value === undefined || variable.value.length === 0) { + variable.value = ""; + requiredVariables.push(variable); + } + + variable.index = key; + } } if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length !== 0) { - for (let [key,keyval] in Object.entries(workflow.triggers)) { - var trigger = workflow.triggers[key]; - trigger.index = key; + for (let [key,keyval] in Object.entries(workflow.triggers)) { + var trigger = workflow.triggers[key]; + trigger.index = key; - if (trigger.trigger_type === "WEBHOOK") { - console.log("Found webhook: ", trigger) - if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") { - console.log("Actions: ", newactions) - const findapp = trigger.app_association.name.toLowerCase() - const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp) + if (trigger.trigger_type === "WEBHOOK") { + console.log("Found webhook: ", trigger) - // Adding webhook to start of it - if (foundindex >= 0) { - const tmpsteps = newactions[foundindex].steps - newactions[foundindex].steps = [ - { - "title": "Configure Webhook", - "type": "webhook", - "required": true, - } - ] + if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") { + console.log("Actions: ", newactions) + const findapp = trigger.app_association.name.toLowerCase() + const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp) - for (let [subkey,subkeyval] in Object.entries(tmpsteps)) { - newactions[foundindex].steps.push(tmpsteps[subkey]) - } - - newactions[foundindex].show_steps = true + // Adding webhook to start of it + if (foundindex >= 0) { + const tmpsteps = newactions[foundindex].steps + newactions[foundindex].steps = [ + { + "title": "Configure Webhook", + "type": "webhook", + "required": true, + } + ] - console.log("CHANGED ACTION: ", newactions[foundindex]) - //console.log("Index: ", newactions[foundindex]) + for (let [subkey,subkeyval] in Object.entries(tmpsteps)) { + newactions[foundindex].steps.push(tmpsteps[subkey]) + } + + newactions[foundindex].show_steps = true - continue - } - } + console.log("CHANGED ACTION: ", newactions[foundindex]) + //console.log("Index: ", newactions[foundindex]) + + continue + } + } + } + + if (trigger.status === "running") { + continue; + } + + if ( + trigger.trigger_type === "SUBFLOW" || + trigger.trigger_type === "USERINPUT" + ) { + continue; + } + + requiredTriggers.push(trigger); } + } - if (trigger.status === "running") { - continue; - } + if (requiredTriggers.length === 0 && requiredVariables.length === 0 && newactions.length === 0 && setConfigureWorkflowModalOpen !== undefined) { + setConfigureWorkflowModalOpen(false); + } - if ( - trigger.trigger_type === "SUBFLOW" || - trigger.trigger_type === "USERINPUT" - ) { - continue; - } - - requiredTriggers.push(trigger); - } -} - - if ( - requiredTriggers.length === 0 && - requiredVariables.length === 0 && - newactions.length === 0 - ) { - setConfigureWorkflowModalOpen(false); - } - - setRequiredTriggers(requiredTriggers); - setRequiredVariables(requiredVariables); - setRequiredActions(newactions); + setRequiredTriggers(requiredTriggers); + setRequiredVariables(requiredVariables); + setRequiredActions(newactions); } if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) { @@ -381,7 +424,7 @@ const ConfigureWorkflow = (props) => { const { trigger } = props return ( - + { {trigger.status !== "running" ? "Start" : "Running"} ) : null} - {/* - - - ) - }} - fullWidth - color="primary" - type={"text"} - placeholder={`New value for ${trigger.name}`} - onChange={(event) => { - console.log("NEW VALUE ON INDEX", trigger.value) - }} - onBlur={(event) => { - //workflow.variables[variable.index] = event.target.value - }} - /> - } - style={{}} - /> - */} ); }; @@ -488,7 +498,7 @@ const ConfigureWorkflow = (props) => { //Name: {variable.name} - {variable.value}. return ( - + @@ -589,88 +599,312 @@ const ConfigureWorkflow = (props) => { }; + const AppSectionSelfcontained = (props) => { + const { action } = props; + + const [opened, setOpened] = useState(false); + const [filled, setFilled] = useState(false); + const [submitted, setSubmitted] = useState(false); + const [finalized, setFinalized] = useState(false); + + const [authFields, setAuthFields] = useState([]) + const [sensitiveFields, setSensitiveFields] = useState([]) + + if (authFields.length === 0 && opened === true) { + // Loop through fields of the action + + var newfields = [] + const params = action.action.parameters + + var sensitiveIndexes = [] + var index = 0 + for (let key in params) { + const param = params[key] + + if (param.configuration === true) { + if (param.name.toLowerCase().includes("key") || param.name.toLowerCase().includes("token") || param.name.toLowerCase().includes("password")) { + sensitiveIndexes.push(index) + } + + newfields.push({ + "key": param.name, + "example": param.example === undefined ? "" : param.example, + "value": param.name === "url" ? param.example : "", + }) + + index += 1 + } + } + + if (newfields.length > 0) { + setSensitiveFields(sensitiveIndexes) + setAuthFields(newfields) + } + } + + + const submitLocalAuth = (app, fields) => { + const appAuthData = { + active: true, + app: app, + fields: fields, + label: "Authentication for " + app.name, + usage: [{"workflow_id": workflow.id}], + auto_distribute: true, + } + + + fetch(globalUrl + "/api/v1/apps/authentication", { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(appAuthData), + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for setting app auth :O!"); + } + + setSubmitted(false) + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + toast("Failed to set app auth: " + responseJson.reason); + } else { + toast("App auth set for app " + app.name.replace("_", " ")); + setFinalized(true) + setOpened(false) + } + }) + .catch((error) => { + setSubmitted(false) + //toast(error.toString()); + console.log("New auth error: ", error.toString()); + }); + } + + var parsedName = action.app_name.replaceAll("_", " "); + if (action.app_name.toLowerCase().endsWith("_api")) { + parsedName = parsedName.substring(0, parsedName.length - 4); + } + + // Remove _basic at the end if it exists + if (parsedName.toLowerCase().endsWith("_basic")) { + parsedName = parsedName.substring(0, parsedName.length - 6); + } + + parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " "); + + return ( + +
+
{ + setOpened(!opened); + }} + > +
+ + {!opened ? : } + + {parsedName} + + {finalized ? "Authenticated" : `Configure ${parsedName}`} + +
+ {filled ? + + : null} +
+ {opened ? +
+ {authFields.map((field, index) => { + var parsedName = field.key + // Remove _basic at the end if it exists + if (parsedName.toLowerCase().endsWith("_basic")) { + parsedName = parsedName.substring(0, parsedName.length - 6); + } + + parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " "); + + return ( +
+ + {parsedName} + + { + event.preventDefault(); + authFields[index].value = event.target.value; + setAuthFields(authFields); + + var allFilled = true; + authFields.forEach((field) => { + if (field.value.length === 0) { + allFilled = false; + } else { + //console.log("Field is not filled: "+field.key) + } + }) + + if (allFilled) { + console.log("Should test the fields, and submit them") + setFilled(true); + } else { + if (filled) { + setFilled(false); + } + } + }} + + endAdornment={ + // Show item that can show field value if password + //field.name.toLowerCase().includes("key") || field.name.toLowerCase().includes("token") || field.name.toLowerCase().includes("password") ? + field.key.toLowerCase().includes("key") || field.key.toLowerCase().includes("token") || field.key.toLowerCase().includes("password") ? + + { + setSensitiveFields(sensitiveFields.filter((item) => item !== index)) + }} + onMouseDown={(event) => { + event.preventDefault(); + }} + > + + + + : null + } + fullWidth + color="primary" + type={sensitiveFields.includes(index) ? "password" : "text"} + placeholder={field.example ? field.example : `Enter your ${field.key}`} + data-lpignore="true" + dataLPIgnore="true" + autocomplete="off" + /> +
+ ) + })} + +
+ : null} +
+
+ ) + } const AppSection = (props) => { const { action } = props; + var parsedName = action.app_name.replaceAll("_", " "); + if (action.app_name.toLowerCase().endsWith("_api")) { + parsedName = parsedName.substring(0, parsedName.length - 4); + } + return ( - {/* - - - {action.app_name} - - - - */} {action.must_authenticate ? - + if (setAuthenticationModalOpen !== undefined) { + setAuthenticationModalOpen(true); + } + }} + > + {action.app_name} + + {action.auth_done ? "Authenticated" : `Authenticate ${action.app_name.replaceAll("_", " ")}`} + + : null} {action.update_version !== action.app_version ? -
: null} @@ -923,10 +1159,10 @@ const ConfigureWorkflow = (props) => {
{clicked === true ? data.steps.map((step, index) => { - var finished = false + var filled = false if (step.type === "activate") { if (data.activation_done === true) { - finished = true + filled = true if (index === activeStep && firstRun === true) { setActiveStep(activeStep+1) @@ -941,10 +1177,10 @@ const ConfigureWorkflow = (props) => { if (step.type === "authenticate") { console.log("AUTH STEP: ", step) if (data.must_authenticate === true ) { - finished = false + filled = false } else { if (data.activation_done === true && data.auth_done === true) { - finished = true + filled = true if (firstRun) { setFinishCount(finishCount+1) @@ -963,7 +1199,7 @@ const ConfigureWorkflow = (props) => { if (exec.execution_argument !== undefined && exec.execution_argument !== null && exec.execution_argument.length > 0 && exec.execution_source === "webhook") { //console.log("Done: ", exec) - finished = true + filled = true if (index === activeStep && firstRun === true) { setActiveStep(activeStep+1) @@ -991,7 +1227,7 @@ const ConfigureWorkflow = (props) => { } return ( - + ) }) : null} @@ -1002,36 +1238,48 @@ const ConfigureWorkflow = (props) => { const topColor = "#f86a3e, #fc3922" return (
-
-
-
- {workflow.name} - - The following configuration makes the workflow ready immediately. + {setConfigureWorkflowModalOpen !== undefined ? +
+ : null} +
+ + + {setConfigureWorkflowModalOpen !== undefined ? + {workflow.name} + : null + } + + + Please configure the following apps for automatic startup of automation: {requiredActions.length > 0 ? ( - - Required Actions - + {setConfigureWorkflowModalOpen !== undefined ? + + Required Actions + + : null} - + {requiredActions.map((data, index) => { return ( -
- {data.steps !== undefined && data.steps !== null && data.show_steps === true ? - - : - - } -
- ) +
+ {data.steps !== undefined && data.steps !== null && data.show_steps === true && setConfigureWorkflowModalOpen !== undefined ? + + : + setConfigureWorkflowModalOpen !== undefined ? + + : + + } +
+ ) })}
) : null} - {requiredVariables.length > 0 ? ( + {setConfigureWorkflowModalOpen !== undefined && requiredVariables.length > 0 ? ( Variables @@ -1044,7 +1292,7 @@ const ConfigureWorkflow = (props) => { ) : null} - {requiredTriggers.length > 0 && showTriggers !== false ? ( + {setConfigureWorkflowModalOpen !== undefined && requiredTriggers.length > 0 && showTriggers !== false ? ( Triggers @@ -1056,51 +1304,52 @@ const ConfigureWorkflow = (props) => { ) : null} - -
- {showFinalizeAnimation ? - finalize workflow animation { - console.log("Img loaded.") + {setConfigureWorkflowModalOpen !== undefined ? +
+ {showFinalizeAnimation ? + finalize workflow animation { + console.log("Img loaded.") + setTimeout(() => { + console.log("Img closing.") + setConfigureWorkflowModalOpen(false); + }, 1250) + + }}/> + : + + {/* + + */} + - */} - - - } -
-
+ } else { + } + }, 1000) + }} + > + Finalize + + + } +
+ : null} +
); }; diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index 9971ff3d..81dacd85 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -5,6 +5,7 @@ import { MuiChipsInput } from "mui-chips-input"; import UsecaseSearch from "../components/UsecaseSearch.jsx" import WorkflowGrid from "../components/WorkflowGrid.jsx" import dayjs from 'dayjs'; +import WorkflowTemplatePopup from "./WorkflowTemplatePopup.jsx"; import { Badge, @@ -57,7 +58,7 @@ import { } from "@mui/icons-material"; const EditWorkflow = (props) => { - const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props + const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, } = props const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove @@ -143,7 +144,9 @@ const EditWorkflow = (props) => { return null } - const newWorkflow = isEditing === true ? false : true + const newWorkflow = isEditing === true ? false : true + const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) + console.log("PRIO: ", priority) var upload = ""; var total_count = 0 @@ -161,6 +164,7 @@ const EditWorkflow = (props) => { maxWidth: isMobile ? "90%" : 650, minHeight: 400, paddingTop: 25, + paddingLeft: 50, //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, }, @@ -170,7 +174,7 @@ const EditWorkflow = (props) => {
- + {newWorkflow ? "New" : "Editing"} workflow {newWorkflow === true ? null : @@ -193,7 +197,7 @@ const EditWorkflow = (props) => {
}
- + Workflows can be built from scratch, or from templates. Usecases can help you discover next steps, and you can search for them directly. Learn more {showUpload === true ? @@ -480,7 +484,7 @@ const EditWorkflow = (props) => { - + + {newWorkflow === true ? + + + Relevant Workflows + - {newWorkflow === true && name.length > 5 ? + {priority === null || priority === undefined ? null : +
+ 2 ? priority.description.split("&")[0] : ""} + img1={priority.description.split("&").length > 2 ? priority.description.split("&")[1] : ""} + + dstapp={priority.description.split("&").length > 3 ? priority.description.split("&")[2] : ""} + img2={priority.description.split("&").length > 3 ? priority.description.split("&")[3] : ""} + title={priority.name} + description={priority.description.split("&").length > 4 ? priority.description.split("&")[4] : ""} + + apps={apps} + /> +
+ } + +
+ : null} + + {newWorkflow === true && name.length > 2 ?
{ + const { userdata, globalUrl } = props + const [activeUsecases, setActiveUsecases] = useState(0); + const [modalOpen, setModalOpen] = React.useState(false); + const [suggestedUsecases, setSuggestedUsecases] = useState([]) + const [usecasesSet, setUsecasesSet] = useState(false) + const [apps, setApps] = useState([]) + const sizing = 475 + + let navigate = useNavigate(); + + const imagestyle = { + height: 40, + borderRadius: 40, + //border: "2px solid rgba(255,255,255,0.3)", + } + + const loadApps = () => { + fetch(`${globalUrl}/api/v1/apps`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + return response.json(); + }) + .then((responseJson) => { + if (responseJson === null) { + console.log("null-response from server") + const pretend_apps = [{ + "name": "TBD", + "app_name": "TBD", + "app_version": "TBD", + "description": "TBD", + "version": "TBD", + "large_image": "", + }] + + setApps(pretend_apps) + return + } + + if (responseJson.success === false) { + console.log("error loading apps: ", responseJson) + return + } + + setApps(responseJson); + }) + .catch((error) => { + console.log("App loading error: " + error.toString()); + }) + } + + // Find priorities in userdata.priorities and check if the item.type === "usecase" + // If so, set the item.isActive to true + if (usecasesSet === false && userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && suggestedUsecases.length === 0) { + + var tmpUsecases = [] + for (let i = 0; i < userdata.priorities.length; i++) { + if (userdata.priorities[i].type !== "usecase" || userdata.priorities[i].active === false) { + continue + } + + tmpUsecases.push(userdata.priorities[i]) + } + + setSuggestedUsecases(tmpUsecases) + setUsecasesSet(true) + loadApps() + } + + const modalView = ( + // console.log("key:", dataValue.key), + //console.log("value:",dataValue.value), + { + setModalOpen(false); + }} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: "800px", + minHeight: "320px", + }, + }} + > + +
+ + Sign Up +
+ + Setup +
+ + Explore +
+ + + Here’s a recommended workflow: + + {/*
+
+
+ + { + slidePrev() + }} + > + + + +
+ +
+ + { + slideNext() + }} + > + + + +
+
+
*/} + + + + +
+ ); + + + return ( +
+ {modalView} + + Start using workflows + + + Based on what you selected here’s our recommendations! + + +
+
+
+ + {suggestedUsecases.length === 0 && usecasesSet ? + + All Workflows are already added for your current apps! + + : + suggestedUsecases.map((priority, index) => { + + const srcapp = priority.description.split("&")[0] + var image1 = priority.description.split("&")[1] + var image2 = "" + var dstapp = "" + if (priority.description.split("&").length > 3) { + dstapp = priority.description.split("&")[2] + image2 = priority.description.split("&")[3] + } + + const name = priority.name.replace("Suggested Usecase: ", "") + + var description = "" + if (priority.description.split("&").length > 4) { + description = priority.description[4] + } + + // FIXME: Should have a proper description + description = "" + + return ( + + ) + })} + +
+
+ + + +
+ + + Explore usecases + + +
+
+
+
+
+ ) +} +export default ExploreWorkflow diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 5f66af70..d3137674 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -212,7 +212,8 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho const notificationWidth = 300 const imagesize = 22; - const boxColor = "#86c142"; + const boxColor = "#86c142"; + const NotificationItem = (props) => { const {data} = props @@ -263,7 +264,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho {data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ? - {data.title} + {data.title} ({data.amount}) : @@ -277,7 +278,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho : null } - + {data.description} {/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ? diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index d963ec0b..c069499f 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -2278,30 +2278,30 @@ const ParsedAction = (props) => { handleItemClick([innerdata]); }} > - - { - //console.log("HOVER: ", pathdata); - }} - onClick={() => { - handleItemClick([innerdata]); - }} - > - - {innerdata.name} - - + + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + + {innerdata.name} + + {parsedPaths.map((pathdata, index) => { // FIXME: Should be recursive in here // diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 5e1c1943..e66784ab 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -69,7 +69,7 @@ const Priority = (props) => { return ( -
+
{priority.type === "usecase" || priority.type == "apps" ? : null} diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index fda866b2..5fb9b152 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -26,22 +26,21 @@ import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx"; import { FullscreenExit as FullscreenExitIcon, Extension as ExtensionIcon, - Apps as AppsIcon, - FavoriteBorder as FavoriteBorderIcon, - Schedule as ScheduleIcon, - FormatListNumbered as FormatListNumberedIcon, + Apps as AppsIcon, + FavoriteBorder as FavoriteBorderIcon, + Schedule as ScheduleIcon, + FormatListNumbered as FormatListNumberedIcon, SquareFoot as SquareFootIcon, - Circle as CircleIcon, - Add as AddIcon, + Circle as CircleIcon, + Add as AddIcon, PlayArrow as PlayArrowIcon, -} from '@mui/icons-material'; - -import { AutoFixHigh as AutoFixHighIcon, + Close as CloseIcon, CompressOutlined, QrCodeScannerOutlined, } from '@mui/icons-material'; + import { validateJson } from "../views/Workflows.jsx"; import ReactJson from "react-json-view"; import PaperComponent from "../components/PaperComponent.jsx"; @@ -823,10 +822,6 @@ const CodeEditor = (props) => { } const executeSingleAction = (inputdata) => { - //if (serverside === true) { - // return - //} - if (validation === true) { inputdata = JSON.stringify(inputdata) } @@ -834,7 +829,10 @@ const CodeEditor = (props) => { // Shuffle Tools 1.2.0 (in most cases?) const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a" - const actiondata = {"description":"Repeats the call parameter","id":"","name":"repeat_back_to_me","label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters":[{"description":"The message to repeat","id":"","name":"call","example":"REPEATING: Hello world","value":inputdata,"multiline":true,"options":null,"action_field":"","variant":"STATIC_VALUE","required":true,"configuration":false,"tags":null,"schema":{"type":"string"},"skip_multicheck":false,"value_replace":null,"unique_toggled":false,"autocompleted":false}],"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 actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : "repeat_back_to_me" + const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : [{"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":{}} setExecutionResult({ "valid": false, @@ -924,6 +922,20 @@ const CodeEditor = (props) => { }, }} > + { + setExpansionModalOpen(false) + }} + > + +
{ isFileEditor ? @@ -1555,7 +1567,7 @@ const CodeEditor = (props) => { { executeSingleAction(expOutput) }}> - + {executing ? : } diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index 83d43e2b..7cc8f052 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -1,78 +1,106 @@ import React, { useState, useEffect } from "react"; -import ReactGA from 'react-ga4'; -import Checkbox from '@mui/material/Checkbox'; +import ReactGA from "react-ga4"; +import Checkbox from "@mui/material/Checkbox"; -import AliceCarousel from 'react-alice-carousel'; -import 'react-alice-carousel/lib/alice-carousel.css'; +import AliceCarousel from "react-alice-carousel"; +import "react-alice-carousel/lib/alice-carousel.css"; -import SearchIcon from '@mui/icons-material/Search'; -import EmailIcon from '@mui/icons-material/Email'; -import NewReleasesIcon from '@mui/icons-material/NewReleases'; -import ExtensionIcon from '@mui/icons-material/Extension'; -import LightbulbIcon from '@mui/icons-material/Lightbulb'; -import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew'; -import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; - -import theme from '../theme.jsx'; +import SearchIcon from "@mui/icons-material/Search"; +import EmailIcon from "@mui/icons-material/Email"; +import NewReleasesIcon from "@mui/icons-material/NewReleases"; +import ExtensionIcon from "@mui/icons-material/Extension"; +import LightbulbIcon from "@mui/icons-material/Lightbulb"; +import TrendingFlatIcon from "@mui/icons-material/TrendingFlat"; +import theme from "../theme.jsx"; +import CheckBoxSharpIcon from "@mui/icons-material/CheckBoxSharp"; +import AppSearch from "../components/Appsearch.jsx"; +import CloseIcon from "@mui/icons-material/Close"; import { - Button, - Collapse, - IconButton, - FormGroup, - FormControl, - InputLabel, - FormLabel, - FormControlLabel, - Select, - MenuItem, - Grid, - Paper, - Typography, - TextField, - Zoom, - List, - ListItem, - ListItemText, - Divider, - Tooltip, - Chip, - ButtonGroup, + Button, + Collapse, + IconButton, + FormGroup, + FormControl, + InputLabel, + FormLabel, + FormControlLabel, + Select, + MenuItem, + Grid, + Paper, + Typography, + TextField, + Zoom, + List, + ListItem, + ListItemText, + Divider, + Tooltip, + Chip, + ButtonGroup, } from "@mui/material"; -//import { useAlert +//import { useAlert import { useNavigate, Link } from "react-router-dom"; -import WorkflowSearch from '../components/Workflowsearch.jsx'; -import AuthenticationItem from '../components/AuthenticationItem.jsx'; -import WorkflowPaper from "../components/WorkflowPaper.jsx" -import UsecaseSearch from "../components/UsecaseSearch.jsx" - +import WorkflowSearch from "../components/Workflowsearch.jsx"; +import AuthenticationItem from "../components/AuthenticationItem.jsx"; +import WorkflowPaper from "../components/WorkflowPaper.jsx"; +import UsecaseSearch from "../components/UsecaseSearch.jsx"; +import ExploreWorkflow from "../components/ExploreWorkflow.jsx"; const responsive = { - 0: { items: 1 }, + 0: { items: 1 }, +}; + +const imagestyle = { + height: 40, + borderRadius: 40, + //border: "2px solid rgba(255,255,255,0.3)", }; const WelcomeForm = (props) => { - const { userdata, globalUrl, discoveryWrapper, setDiscoveryWrapper, appFramework, getFramework, activeStep, setActiveStep, steps, skipped, setSkipped, getApps, apps, handleSetSearch, usecaseButtons, defaultSearch, setDefaultSearch, selectionOpen, setSelectionOpen, } = props + const { + userdata, + globalUrl, + discoveryWrapper, + setDiscoveryWrapper, + appFramework, + getFramework, + activeStep, + setActiveStep, + steps, + skipped, + setSkipped, + getApps, + apps, + handleSetSearch, + usecaseButtons, + defaultSearch, + setDefaultSearch, + selectionOpen, + setSelectionOpen, + } = props; + const [isActive, setIsActive] = useState(0); - const [usecaseItems, setUsecaseItems] = useState([ - { - "search": "Phishing", - "usecase_search": undefined, - }, - { - "search": "Enrichment", - "usecase_search": undefined, - }, - { - "search": "Enrichment", - "usecase_search": "SIEM alert enrichment", - }, - { - "search": "Build your own", - "usecase_search": undefined, - }]) - - /* + const [usecaseItems, setUsecaseItems] = useState([ + { + search: "Phishing", + usecase_search: undefined, + }, + { + search: "Enrichment", + usecase_search: undefined, + }, + { + search: "Enrichment", + usecase_search: "SIEM alert enrichment", + }, + { + search: "Build your own", + usecase_search: undefined, + }, + ]); + /*
{ const [orgType, setOrgType] = React.useState("") const [finishedApps, setFinishedApps] = React.useState([]) const [authentication, setAuthentication] = React.useState([]); - const [newSelectedApp, setNewSelectedApp] = React.useState({}) - const [thumbIndex, setThumbIndex] = useState(0); + const [newSelectedApp, setNewSelectedApp] = React.useState({}) + const [thumbIndex, setThumbIndex] = useState(0); const [thumbAnimation, setThumbAnimation] = useState(false); const [clickdiff, setclickdiff] = useState(0); - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; //const alert = useAlert(); let navigate = useNavigate(); - const onNodeSelect = (label) => { - if (setDiscoveryWrapper !== undefined) { - setDiscoveryWrapper( - {"id": label} - ) - } + const iconStyles = { + color: "rgba(255, 255, 255, 1)", + }; - if (isCloud) { - ReactGA.event({ - category: "welcome", - action: `click_${label}`, - label: "", - }) - } - - setSelectionOpen(true) - setDefaultSearch(label) + const onNodeSelect = (label) => { + if (setDiscoveryWrapper !== undefined) { + setDiscoveryWrapper({ id: label }); } - useEffect(() => { - if (userdata.id === undefined) { - return - } - - if (userdata.name !== undefined && userdata.name !== null && userdata.name.length > 0) { - setName(userdata.name) - } - - if (userdata.active_org !== undefined && userdata.active_org.name !== undefined && userdata.active_org.name !== null && userdata.active_org.name.length > 0) { - setOrgName(userdata.active_org.name) - } - }, [userdata]) - - useEffect(() => { - if (discoveryWrapper === undefined || discoveryWrapper.id === undefined) { - setDefaultSearch("") - var newfinishedApps = finishedApps - newfinishedApps.push(defaultSearch) - setFinishedApps(finishedApps) - } - }, [discoveryWrapper]) - - useEffect(() => { - if ( - 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 && !isNaN(foundTab)) { - if (foundTab === 3 || foundTab === "3") { - //console.log("Set search!") - } - } else { - //navigate(`/welcome?tab=1`) - } - - const foundTemplate = params["workflow_template"]; - if (foundTemplate !== null && foundTemplate !== undefined) { - console.log("Found workflow template: ", foundTemplate) - - var sourceapp = undefined - var destinationapp = undefined - var action = undefined - const srcapp = params["source_app"]; - if (srcapp !== null && srcapp !== undefined) { - sourceapp = srcapp - } - - const dstapp = params["dest_app"]; - if (dstapp !== null && dstapp !== undefined) { - destinationapp = dstapp - } - - const act = params["action"]; - if (act !== null && act !== undefined) { - action = act - } - - //defaultSearch={foundTemplate} - // - usecaseItems[0] = { - "search": "enrichment", - "usecase_search": foundTemplate, - "sourceapp": sourceapp, - "destinationapp": destinationapp, - "autotry": action === "try", - } - - console.log("Adding: ", usecaseItems[0]) - - setUsecaseItems(usecaseItems) - } - } - }, []) - - const isStepOptional = step => { - return step === 1 + if (isCloud) { + ReactGA.event({ + category: "welcome", + action: `click_${label}`, + label: "", + }); } - const sendUserUpdate = (name, role, userId) => { - const data = { - "tutorial": "welcome", - "firstname": name, - "company_role": role, - "user_id": userId, - } + setSelectionOpen(true); + setDefaultSearch(label); + }; - const url = `${globalUrl}/api/v1/users/updateuser` - 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) { - console.log("Update user success") - //toast("Failed updating org: ", responseJson.reason); - } else { - console.log("Update success!") - //toast("Successfully edited org!"); - } - }) - ) - .catch((error) => { - console.log("Update err: ", error.toString()) - //toast("Err: " + error.toString()); - }); - } - - const sendOrgUpdate = (orgname, company_type, orgId, priority) => { - var data = { - org_id: orgId, - }; - - if (orgname.length > 0) { - data.name = orgname - } - - if (company_type.length > 0) { - data.company_type = company_type - } - - if (priority.length > 0) { - data.priority = priority - } - - 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((response) => - response.json().then((responseJson) => { - if (responseJson["success"] === false) { - console.log("Update of org failed") - //toast("Failed updating org: ", responseJson.reason); - } else { - //toast("Successfully edited org!"); - } - }) - ) - .catch((error) => { - console.log("Update err: ", error.toString()) - //toast("Err: " + error.toString()); - }); - } - - var workflowDelay = -50 - const NewHits = ({ hits }) => { - const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) - var counted = 0 - - const paperAppContainer = { - display: "flex", - flexWrap: "wrap", - alignContent: "space-between", - marginTop: 5, - } - - return ( - - {hits.map((data, index) => { - workflowDelay += 50 - - if (index > 3) { - return null - } - - return ( - - - - - - ) - })} - - ) - } - - const isStepSkipped = step => { - return skipped.has(step) + useEffect(() => { + if (userdata.id === undefined) { + return; } - const handleNext = () => { - setDefaultSearch("") + if ( + userdata.name !== undefined && + userdata.name !== null && + userdata.name.length > 0 + ) { + setName(userdata.name); + } - if (activeStep === 0) { - console.log("Should send basic information about org (fetch)") - setclickdiff(240) - navigate(`/welcome?tab=2`) - - if (isCloud) { - ReactGA.event({ - category: "welcome", - action: "click_page_one_next", - label: "", - }) - } - - if (userdata.active_org !== undefined && userdata.active_org.id !== undefined && userdata.active_org.id !== null && userdata.active_org.id.length > 0) { - sendOrgUpdate(orgName, orgType, userdata.active_org.id, "") - } + if ( + userdata.active_org !== undefined && + userdata.active_org.name !== undefined && + userdata.active_org.name !== null && + userdata.active_org.name.length > 0 + ) { + setOrgName(userdata.active_org.name); + } + }, [userdata]); - if (userdata.id !== undefined && userdata.id !== null && userdata.id.length > 0) { - sendUserUpdate(name, role, userdata.id) - } + useEffect(() => { + if (discoveryWrapper === undefined || discoveryWrapper.id === undefined) { + setDefaultSearch(""); + var newfinishedApps = finishedApps; + newfinishedApps.push(defaultSearch); + setFinishedApps(finishedApps); + } + }, [discoveryWrapper]); - } else if (activeStep === 1) { - console.log("Should send secondary info about apps and other things") - setDiscoveryWrapper({}) - - navigate(`/welcome?tab=3`) - //handleSetSearch("Enrichment", "2. Enrich") - handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase) - getApps() + useEffect(() => { + if ( + 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 && !isNaN(foundTab)) { + if (foundTab === 3 || foundTab === "3") { + //console.log("Set search!") + } + } else { + //navigate(`/welcome?tab=1`) + } - // Make sure it's up to date - if (getFramework !== undefined) { - getFramework() - } - } else if (activeStep === 2) { - console.log("Should send third page with workflows activated and the like") - } + const foundTemplate = params["workflow_template"]; + if (foundTemplate !== null && foundTemplate !== undefined) { + console.log("Found workflow template: ", foundTemplate); - - let newSkipped = skipped; - if (isStepSkipped(activeStep)) { - newSkipped = new Set(newSkipped.values()); - newSkipped.delete(activeStep); + var sourceapp = undefined; + var destinationapp = undefined; + var action = undefined; + const srcapp = params["source_app"]; + if (srcapp !== null && srcapp !== undefined) { + sourceapp = srcapp; } - setActiveStep(prevActiveStep => prevActiveStep + 1); - setSkipped(newSkipped); + const dstapp = params["dest_app"]; + if (dstapp !== null && dstapp !== undefined) { + destinationapp = dstapp; + } + + const act = params["action"]; + if (act !== null && act !== undefined) { + action = act; + } + + //defaultSearch={foundTemplate} + // + usecaseItems[0] = { + search: "enrichment", + usecase_search: foundTemplate, + sourceapp: sourceapp, + destinationapp: destinationapp, + autotry: action === "try", + }; + + console.log("Adding: ", usecaseItems[0]); + + setUsecaseItems(usecaseItems); + } } + }, []); - const handleBack = () => { - setActiveStep(prevActiveStep => prevActiveStep - 1); + const isStepOptional = (step) => { + return step === 1; + }; - if (activeStep === 2) { - setDiscoveryWrapper({}) - - if (getFramework !== undefined) { - getFramework() - } - navigate("/welcome?tab=2") - } else if (activeStep === 1) { - navigate("/welcome?tab=1") - } + const sendUserUpdate = (name, role, userId) => { + const data = { + tutorial: "welcome", + firstname: name, + company_role: role, + user_id: userId, }; - const handleSkip = () => { - setclickdiff(240) - if (!isStepOptional(activeStep)) { - throw new Error("You can't skip a step that isn't optional."); - } - setActiveStep(prevActiveStep => prevActiveStep + 1); - setSkipped(prevSkipped => { - const newSkipped = new Set(prevSkipped.values()); - newSkipped.add(activeStep); - return newSkipped; + const url = `${globalUrl}/api/v1/users/updateuser`; + 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) { + console.log("Update user success"); + //toast("Failed updating org: ", responseJson.reason); + } else { + console.log("Update success!"); + //toast("Successfully edited org!"); + } + }) + ) + .catch((error) => { + console.log("Update err: ", error.toString()); + //toast("Err: " + error.toString()); + }); + }; + + const sendOrgUpdate = (orgname, company_type, orgId, priority) => { + var data = { + org_id: orgId, + }; + + if (orgname.length > 0) { + data.name = orgname; + } + + if (company_type.length > 0) { + data.company_type = company_type; + } + + if (priority.length > 0) { + data.priority = priority; + } + + 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((response) => + response.json().then((responseJson) => { + if (responseJson["success"] === false) { + console.log("Update of org failed"); + //toast("Failed updating org: ", responseJson.reason); + } else { + //toast("Successfully edited org!"); + } + }) + ) + .catch((error) => { + console.log("Update err: ", error.toString()); + //toast("Err: " + error.toString()); + }); + }; + + var workflowDelay = -50; + const NewHits = ({ hits }) => { + const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); + var counted = 0; + + const paperAppContainer = { + display: "flex", + flexWrap: "wrap", + alignContent: "space-between", + marginTop: 5, + }; + + return ( + + {hits.map((data, index) => { + workflowDelay += 50; + + if (index > 3) { + return null; + } + + return ( + + + + + + ); + })} + + ); + }; + + const isStepSkipped = (step) => { + return skipped.has(step); + }; + + const handleNext = () => { + setDefaultSearch(""); + + if (activeStep === 0) { + console.log("Should send basic information about org (fetch)"); + setclickdiff(240); + navigate(`/welcome?tab=2`); + + if (isCloud) { + ReactGA.event({ + category: "welcome", + action: "click_page_one_next", + label: "", }); - }; + } - const handleReset = () => { - setActiveStep(0); - }; + if ( + userdata.active_org !== undefined && + userdata.active_org.id !== undefined && + userdata.active_org.id !== null && + userdata.active_org.id.length > 0 + ) { + sendOrgUpdate(orgName, orgType, userdata.active_org.id, ""); + } - useEffect(() => { - console.log("Selected app changed (effect)") - }, [newSelectedApp]) + if ( + userdata.id !== undefined && + userdata.id !== null && + userdata.id.length > 0 + ) { + sendUserUpdate(name, role, userdata.id); + } + } else if (activeStep === 1) { + console.log("Should send secondary info about apps and other things"); + setDiscoveryWrapper({}); - //const buttonWidth = 145 - const buttonWidth = 450 - const buttonMargin = 10 - const sizing = 475 - const buttonStyle = { - flex: 1, - width: "100%", - padding: 25, - margin: buttonMargin, - fontSize: 18, - } + navigate(`/welcome?tab=3`); + //handleSetSearch("Enrichment", "2. Enrich") + handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase); + getApps(); - const slideNext = () => { - if (!thumbAnimation && thumbIndex < usecaseItems.length - 1) { - //handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase) - setThumbIndex(thumbIndex + 1); - } else if (!thumbAnimation && thumbIndex === usecaseItems.length - 1) { - setThumbIndex(0) - } - }; + // Make sure it's up to date + if (getFramework !== undefined) { + getFramework(); + } + } else if (activeStep === 2) { + console.log( + "Should send third page with workflows activated and the like" + ); + } - const slidePrev = () => { - if (!thumbAnimation && thumbIndex > 0) { - setThumbIndex(thumbIndex - 1); - } else if (!thumbAnimation && thumbIndex === 0) { - setThumbIndex(usecaseItems.length-1) - } - }; + let newSkipped = skipped; + if (isStepSkipped(activeStep)) { + newSkipped = new Set(newSkipped.values()); + newSkipped.delete(activeStep); + } - const newButtonStyle = { - padding: 22, - flex: 1, - margin: buttonMargin, - minWidth: buttonWidth, - maxWidth: buttonWidth, - } + setActiveStep((prevActiveStep) => prevActiveStep + 1); + setSkipped(newSkipped); + }; + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); - const formattedCarousel = appFramework === undefined || appFramework === null ? [] : usecaseItems.map((item, index) => { - return ( -
- -
- ) - }) + if (activeStep === 2) { + setDiscoveryWrapper({}); - const getStepContent = (step) => { - switch (step) { - case 0: - return ( - - - {/*isCloud ? null : + if (getFramework !== undefined) { + getFramework(); + } + navigate("/welcome?tab=2"); + } else if (activeStep === 1) { + navigate("/welcome?tab=1"); + } + }; + + const handleSkip = () => { + setclickdiff(240); + if (!isStepOptional(activeStep)) { + throw new Error("You can't skip a step that isn't optional."); + } + setActiveStep((prevActiveStep) => prevActiveStep + 1); + setSkipped((prevSkipped) => { + const newSkipped = new Set(prevSkipped.values()); + newSkipped.add(activeStep); + return newSkipped; + }); + }; + + const handleReset = () => { + setActiveStep(0); + }; + + useEffect(() => { + console.log("Selected app changed (effect)"); + }, [newSelectedApp]); + + //const buttonWidth = 145 + const buttonWidth = 450; + const buttonMargin = 10; + const sizing = 475; + const bottomButtonStyle = { + borderRadius: 200, + height: 51, + width: 464, + fontSize: 16, + // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", + background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", + padding: "16px 24px", + // top: 20, + margin: "auto", + itemAlign: "center", + // marginLeft: "65px", + }; + + const buttonStyle = { + flex: 1, + width: 224, + padding: 25, + margin: buttonMargin, + color : "var(--White-text, #F1F1F1)", + fontWeight: 400, + fontSize: 16, + background: "rgba(33, 33, 33, 1)", + borderColor: "rgba(33, 33, 33, 1)", + borderRadius: 8, + }; + + const slideNext = () => { + if (!thumbAnimation && thumbIndex < usecaseItems.length - 1) { + //handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase) + setThumbIndex(thumbIndex + 1); + } else if (!thumbAnimation && thumbIndex === usecaseItems.length - 1) { + setThumbIndex(0); + } + }; + + const slidePrev = () => { + if (!thumbAnimation && thumbIndex > 0) { + setThumbIndex(thumbIndex - 1); + } else if (!thumbAnimation && thumbIndex === 0) { + setThumbIndex(usecaseItems.length - 1); + } + }; + + const newButtonStyle = { + padding: 22, + flex: 1, + margin: buttonMargin, + minWidth: buttonWidth, + maxWidth: buttonWidth, + }; + + const formattedCarousel = + appFramework === undefined || appFramework === null + ? [] + : usecaseItems.map((item, index) => { + return ( +
+ +
+ ); + }); + + const getStepContent = (step) => { + switch (step) { + case 0: + return ( + + + {/*isCloud ? null : This data will be used within the product and NOT be shared unless cloud synchronization is configured. */} - - In order to understand how we best can help you find relevant Usecases, please provide the information below. This is optional, but highly encouraged. - - - { - setName(e.target.value) - }} - /> - - - { - setOrgName(e.target.value) - }} - /> - - - - Your Role - - - - - - Company Type - - - - - - ) - case 1: - return ( - -
- + + In order to understand how we best can help you find relevant + Usecases, please provide the information below. This is + optional, but highly encouraged. + + + { + setName(e.target.value); + }} + /> + + + { + setOrgName(e.target.value); + }} + /> + + + + + Your Role + + + + + + + + Company Type + + + + + + + ); + case 1: + return ( + +
+ {selectionOpen ? ( +
+
+ {defaultSearch} + +
+
+ +
+ ) : null} + {/* Apps for each category are shown based on your activity and can be changed by clicking their icon. We will help you connect them later. - - {/*The app framework helps us access and authenticate the most important APIs for you. */} + */} + {/*
+ { + navigate("/welcome") + }}/> + { + navigate("/welcome") + }}> + Back + +
*/} + + Find your apps + + + Select the apps you work with and we will connect the for you. + + {/*The app framework helps us access and authenticate the most important APIs for you. */} - {/* + {/* What is your development experience? @@ -612,39 +821,104 @@ const WelcomeForm = (props) => { */} - - {/*Find your integrations!*/} -
- -
-
- - -
-
- - -
- {/* + + {/*Find your integrations!*/} +
+ +
+
+ + +
+
+ + +
+ {/* What do you want to automate first ? { */}
- {/* - - - What tools do you use? - - - - */} -
- +
+
+ +
+
+ ) case 2: return (
- - These are some of our Workflow templates, used to start new Workflows. Use the right and left buttons to find new Usecases, and click the orange button to build it. -
- - { - slidePrev() - }} - > - - - -
- +
- - { - slideNext() - }} - > - - - -
-
+
+
) @@ -762,36 +977,39 @@ const WelcomeForm = (props) => { } } - const extraHeight = isCloud ? -7 : 0 - return ( -
- {/*selectionOpen ? + + const extraHeight = isCloud ? -7 : 0; + return ( +
+ {/*selectionOpen ? : null*/} -
- {activeStep === steps.length ? ( -
- You Will be Redirected to getting Start Page Wait for 5-sec. - - - -
- ) : ( -
- {getStepContent(activeStep)} -
+
+ {activeStep === steps.length ? ( +
+ You Will be Redirected to getting Start Page Wait for 5-sec. + + + +
+ ) : ( +
+ {getStepContent(activeStep)} + {/*
{activeStep === 2 || activeStep === 1 ?
- {/*isStepOptional(activeStep) && ( + // (commented) isStepOptional(activeStep) && ( - )*/} + ) //commented : null}
- } -
- )} -
-
- ); -} + }*/} +
+ )} +
+
+ ); +}; -export default WelcomeForm +export default WelcomeForm; diff --git a/frontend/src/components/WorkflowGrid.jsx b/frontend/src/components/WorkflowGrid.jsx index f99bbc52..589e4f7d 100644 --- a/frontend/src/components/WorkflowGrid.jsx +++ b/frontend/src/components/WorkflowGrid.jsx @@ -154,7 +154,6 @@ const AppGrid = props => { }) .then((responseJson) => { if (responseJson.success !== false) { - console.log("Usecases: ", responseJson) //handleKeysetting(responseJson, workflows) } }) @@ -187,10 +186,10 @@ const AppGrid = props => { }, []) if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) { - console.log("In refinement: ", inputsearch) //setLocalMessage(inputsearch) refine(inputsearch) defaultSearch = inputsearch + return null } else if (onlyResults === true) { // Don't return anything unless refinement works return null @@ -244,9 +243,7 @@ const AppGrid = props => { return (
{onlyResults === true && hits.length > 0 ? - - Relevant Workflows - + null : null} {hits.map((data, index) => { diff --git a/frontend/src/components/WorkflowPaper.jsx b/frontend/src/components/WorkflowPaper.jsx index e07daca6..7ab4c546 100644 --- a/frontend/src/components/WorkflowPaper.jsx +++ b/frontend/src/components/WorkflowPaper.jsx @@ -277,7 +277,7 @@ const WorkflowPaper = (props) => { > {data.tags !== undefined && data.tags !== null ? data.tags.map((tag, index) => { - if (index >= 3) { + if (index >= 2) { return null; } diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx new file mode 100644 index 00000000..769ade88 --- /dev/null +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -0,0 +1,382 @@ +import React, { useState, useEffect } from "react"; + +import { toast } from "react-toastify" +import theme from '../theme.jsx'; +import { + Button, + Typography, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Drawer, + CircularProgress, + IconButton, +} from "@mui/material"; + +import { + Check as CheckIcon, + TrendingFlat as TrendingFlatIcon, + Close as CloseIcon, +} from '@mui/icons-material'; + +import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx"; +import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx"; + +const WorkflowTemplatePopup = (props) => { + const { userdata, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps } = props; + + const [isActive, setIsActive] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [modalOpen, setModalOpen] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); + const [workflowLoading, setWorkflowLoading] = useState(false); + const [workflow, setWorkflow] = useState({}); + + const [appAuthentication, setAppAuthentication] = React.useState(undefined); + const imagestyle = { + height: 40, + width: 40, + borderRadius: 40, + } + + if (title === undefined || title === null || title === "") { + console.log("No title for workflow template popup!"); + return null + } + + const getWorkflow = (workflowId) => { + fetch(`${globalUrl}/api/v1/workflows/${workflowId}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + console.log("Error in workflow loading for ID ", workflowId) + } else { + setWorkflow(responseJson) + } + }) + .catch((error) => { + console.log("err in framework: ", error.toString()); + setWorkflowLoading(false) + }) + } + + const loadAppAuth = () => { + 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 setting app auth :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (!responseJson.success) { + toast("Failed to get app auth: " + responseJson.reason); + return + } + + var newauth = []; + for (let authkey in responseJson.data) { + if (responseJson.data[authkey].defined === false) { + continue; + } + + newauth.push(responseJson.data[authkey]); + } + + setAppAuthentication(newauth); + }) + .catch((error) => { + //toast(error.toString()); + console.log("New auth error: ", error.toString()); + }); + } + + const getGeneratedWorkflow = () => { + // POST + // https://shuffler.io/api/v1/workflows/merge + // destination: {app_id: "b9c2feaf99b6309dabaeaa8518c61d3d", app_name: "Servicenow_API", app_version: "",…} + // id: "" + // middle:[] + // name: "Email analysis" + // source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name + + setWorkflowLoading(true) + + + // FIXME: Remove hardcoding here after testing, and user srcapp/dstapp + const newsrcapp = srcapp + const newdstapp = dstapp + + const mergedata = { + name: title, + id: "", + source: { + app_name: newsrcapp, + }, + middle: [], + destination: { + app_name: newdstapp, + }, + } + + fetch(globalUrl + "/api/v1/workflows/merge", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(mergedata), + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for framework!"); + } + + setWorkflowLoading(false) + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + console.log("Error in workflow template: ", responseJson.error); + + setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.") + + setIsActive(true) + //setTimeout(() => { + // setModalOpen(false) + //}, 5000) + } else { + console.log("Success in workflow template: ", responseJson); + setIsActive(true) + if (responseJson.workflow_id === "") { + console.log("Failed to build workflow for these tools. Closing in 3 seconds.") + return + } + + getWorkflow(responseJson.workflow_id) + } + }) + .catch((error) => { + console.log("err in framework: ", error.toString()); + setWorkflowLoading(false) + }) + } + + const isFinished = () => { + // Look for configuration fields being done in the current modal + // 1. Start by finding the modal + const template = document.getElementById("workflow-template") + if (template === null || template == undefined) { + return true + } + + // Find item in template with id app-config + const appconfig = template.getElementsByClassName("app-config") + if (appconfig === null || appconfig == undefined) { + return true + } + + console.log("APPCONFIG: ", appconfig) + + return false + } + + const ModalView = () => { + return ( + { + setModalOpen(false); + }} + PaperProps={{ + style: { + backgroundColor: "black", + color: "white", + minWidth: 700, + maxWidth: 700, + paddingTop: 75, + itemAlign: "center", + }, + }} + > + { + setModalOpen(false); + }} + > + + + + + Configure Workflow + + + Selected Workflow: + +
+ +
+ {workflowLoading ? +
+ Generating the Workflow... + + +
+ : +
+ + {errorMessage !== "" ? errorMessage : ""} + +
+ } + + {errorMessage === "" ? + + : null} +
+
+ ) + } + + var parsedTitle = title + const maxlength = 30 + if (title.length > maxlength) { + parsedTitle = title.substring(0, maxlength) + "..." + } + + parsedTitle.replaceAll("_", " ") + + return ( +
+ +
{ + setIsHovered(true) + }} + onMouseLeave={() => { + setIsHovered(false) + }} + onClick={() => { + if (visualOnly === true) { + console.log("Not showing more than visuals.") + return + } + + //setIsActive(!isActive) + if (errorMessage !== "") { + toast("Already failed to generate workflow for these apps. Please try again later or contact support@shuffler.io.") + } else if (isActive) { + toast("Workflow already generated. Please try another template, or continue to the next page.") + + // FIXME: Remove these? + loadAppAuth() + setModalOpen(true) + //getGeneratedWorkflow() + } else { + + loadAppAuth() + setModalOpen(true) + getGeneratedWorkflow() + } + }} + > +
+
+ + {img2 !== "" ? + + + + + : + + } +
+
+ + {parsedTitle} + + + {description} + +
+
+
+ {isActive === true && errorMessage === "" ? + + : ""} +
+
+
+ ) +} + +export default WorkflowTemplatePopup diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index e24f7a0a..8d13070c 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -18,10 +18,12 @@ const theme = createTheme(adaptV4Theme({ secondary: "rgba(255,255,255,0.7)", }, type: "dark", - inputColor: "#383B40", + inputColor: "rgba(39,41,45,1)", + //inputColor: "#383B40", surfaceColor: "#27292d", platformColor: "#1c1c1d", backgroundColor: "#1a1a1a", + green: "#5cc879", borderRadius: 5, defaultBorder: "1px solid rgba(255,255,255,0.3)", jsonTheme: "brewer", @@ -70,11 +72,6 @@ const theme = createTheme(adaptV4Theme({ }, }, overrides: { - MuiPaper: { - root: { - backgroundColor: "#1c1c1d", - }, - }, MuiMenu: { list: { backgroundColor: "#27292d", diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index c8269a52..866f9360 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1692,15 +1692,24 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user const userId = user.id; const data = { user_id: userId }; - fetch(globalUrl + "/api/v1/generateapikey", { + console.log(user, userdata) + + var fetchdata = { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, - body: JSON.stringify(data), credentials: "include", - }) + } + + if (userId === userdata.id) { + fetchdata.method = "GET" + } else { + fetchdata.body = JSON.stringify(data) + } + + fetch(globalUrl + "/api/v1/generateapikey", fetchdata) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for WORKFLOW EXECUTION :O!"); @@ -1889,8 +1898,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user open={selectedUserModalOpen} onClose={() => { setSelectedUserModalOpen(false); - setImage2FA(""); - setSecret2FA(""); + + setImage2FA(""); + setValue2FA(""); + setSecret2FA(""); + setShow2faSetup(false); }} PaperProps={{ style: { @@ -2156,7 +2168,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user /> - Billing + Billing (Beta) /> 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 @@ -3299,7 +3308,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user ) : null; const run2FASetup = (data) => { - console.log("2fa: ", data, show2faSetup); if (!show2faSetup) { get2faCode(data.id); } else { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e8ccdde7..1dd3eb87 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -580,15 +580,39 @@ const AngularWorkflow = (defaultprops) => { const cytoscapeWidth = isMobile ? bodyWidth - leftBarSize : bodyWidth - leftBarSize - 25 const [elements, setElements] = useState([]); + const [loopRunning, setLoopRunning] = useState(false) + + const stop = () => { + setLoopRunning(false) + } + + const start = () => { + setLoopRunning(true) + } + + useEffect(() => { + console.log("In useeffect for loopRunning: ", loopRunning) + if (loopRunning) { + const intervalId = setInterval(() => { + if (!loopRunning) { + clearInterval(intervalId); + } + + fetchUpdates() + }, 3000) + + return () => clearInterval(intervalId); + } + }, [loopRunning]) + // No point going as fast, as the nodes aren't realtime anymore, but bulk updated. - // Set it from 2500 to 6000 to reduce overall load - const { start, stop } = useInterval({ - duration: 3000, - startImmediate: false, - callback: () => { - fetchUpdates(); - }, - }); + //const { start, stop } = useInterval({ + // duration: 3000, + // startImmediate: false, + // callback: () => { + // fetchUpdates(); + // }, + //}); const getAppDocs = (appname, location, version) => { fetch(`${globalUrl}/api/v1/docs/${appname}?location=${location}&version=${version}`, { @@ -924,7 +948,7 @@ const AngularWorkflow = (defaultprops) => { }; const getWorkflowExecution = (id, execution_id) => { - fetch(`${globalUrl}/api/v1/workflows/${id}/executions`, { + fetch(`${globalUrl}/api/v2/workflows/${id}/executions`, { method: "GET", headers: { "Content-Type": "application/json", @@ -940,11 +964,10 @@ const AngularWorkflow = (defaultprops) => { return response.json(); }) .then((responseJson) => { - if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) { - // FIXME: Sort this by time + if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null && responseJson.executions.length > 0) { // - means it's opposite - const newkeys = sortByKey(responseJson, "-started_at"); + const newkeys = sortByKey(responseJson.executions, "-started_at"); setWorkflowExecutions(newkeys); const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; @@ -1021,6 +1044,7 @@ const AngularWorkflow = (defaultprops) => { }, body: JSON.stringify(executionRequest), credentials: "include", + cors: "no-cors", }) .then((response) => { if (response.status !== 200) { @@ -1279,7 +1303,7 @@ const AngularWorkflow = (defaultprops) => { }; const sendStreamRequest = (body) => { - console.log("Stream not activated yet.") + //console.log("Stream not activated yet.") return // Session may be important here huh @@ -15302,6 +15326,10 @@ const AngularWorkflow = (defaultprops) => { overflow: "hidden", }} onMouseOver={() => { + if (cy == undefined || cy == null) { + return + } + var currentnode = cy.getElementById(data.action.id); if (currentnode !== undefined && currentnode !== null && currentnode.length !== 0) { currentnode.addClass("shuffle-hover-highlight"); @@ -15314,6 +15342,10 @@ const AngularWorkflow = (defaultprops) => { //) }} onMouseOut={() => { + if (cy == undefined || cy == null) { + return + } + var currentnode = cy.getElementById(data.action.id); if (currentnode.length !== 0) { currentnode.removeClass("shuffle-hover-highlight"); diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 218f9e39..4f9ac5b3 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -1354,7 +1354,8 @@ const Apps = (props) => { border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", cursor: hover ? "pointer" : "default", textAlign: "center", - height: 125, + minHeight: 150, + maxHeight: 150, }} > {icon} diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index fdffa122..96c14d0e 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -6,6 +6,7 @@ import { makeStyles, } from "@mui/styles"; import classNames from "classnames"; import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; +import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"; // react plugin used to create charts //import { Line, Bar } from "react-chartjs-2"; @@ -90,7 +91,7 @@ const useStyles = makeStyles({ }, }); -const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => { +const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => { const [expandedIndex, setExpandedIndex] = useState(-1); const [expandedItem, setExpandedItem] = useState(-1); const [inputUsecase, setInputUsecase] = useState({}); @@ -103,17 +104,69 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged const [selectedWorkflows, setSelectedWorkflows] = useState([]) const [firstLoad, setFirstLoad] = useState(true) + const [apps, setApps] = useState([]) - const classes = useStyles(); + const classes = useStyles(); let navigate = useNavigate(); const [mitreTags, setMitreTags] = useState([]); + + const loadApps = () => { + fetch(`${globalUrl}/api/v1/apps`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + return response.json(); + }) + .then((responseJson) => { + if (responseJson === null) { + console.log("null-response from server") + const pretend_apps = [{ + "name": "TBD", + "app_name": "TBD", + "app_version": "TBD", + "description": "TBD", + "version": "TBD", + "large_image": "", + }] + + setApps(pretend_apps) + return + } + + if (responseJson.success === false) { + console.log("error loading apps: ", responseJson) + return + } + + setApps(responseJson); + }) + .catch((error) => { + console.log("App loading error: " + error.toString()); + }) + } + + useEffect(() => { + loadApps() + }, []) + if (keys === undefined || keys === null || keys.length === 0) { return null } - const getUsecase = (name, index, subindex) => { - fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(name.replaceAll(" ", "_"))}`, { + const parseUsecase = (parsedUsecase, subcase) => { + console.log("parseUsecase: ", parsedUsecase, subcase) + } + + const getUsecase = (subcase, index, subindex) => { + console.log(subcase) + + fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, { method: "GET", headers: { "Content-Type": "application/json", @@ -129,14 +182,26 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged return response.json(); }) .then((responseJson) => { + var parsedUsecase = responseJson if (responseJson.success === false) { - setInputUsecase({ - "name": name, - }) + parsedUsecase = subcase + + //img1={inputUsecase.srcimg} + //srcapp={inputUsecase.srcapp} + //img2={inputUsecase.dstimg} + //dstapp={inputUsecase.dstapp} + //title={inputUsecase.name} + parsedUsecase = parseUsecase(parsedUsecase, subcase) } else { - setInputUsecase(responseJson) + parsedUsecase = responseJson + + parsedUsecase = parseUsecase(parsedUsecase, responseJson) } + // Look for the type of app and fill in img1, srcapp... + + + setInputUsecase(parsedUsecase) setExpandedIndex(index) setExpandedItem(subindex) @@ -289,6 +354,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged if (selectedItem && subcase.matches.length > 0 && selectedWorkflows.length === 0 && firstLoad === true) { setFirstLoad(false) setSelectedWorkflows(subcase.matches) + } } @@ -316,10 +382,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged } } - //const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color const finished = subcase.matches.length > 0 const backgroundColor = theme.palette.surfaceColor - //"inherit" const itemBorder = `${selectedItem ? "3px" : expandedItem >= 0 ? "0px" : "1px"} solid ${usecase.color}` const fixedName = subcase.name.toLowerCase().replace("_", " ") @@ -330,14 +394,14 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged //setSelectedWorkflows([]) if (selectedItem) { } else { - getUsecase(subcase.name, index, subindex) + getUsecase(subcase, index, subindex) navigate(`/usecases?selected_object=${fixedName}`) //const newitem = removeParam("selected_object", cursearch); //navigate(curpath + newitem) } }}> - { + { }}> {!selectedItem ?
@@ -649,12 +713,12 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged : null} {workflows !== undefined && workflows !== null && workflows.length > 0 ? - option.id === value.id} + getOptionSelected={(option, value) => option.id === value.id} getOptionLabel={(option) => { if ( @@ -685,120 +749,120 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged borderRadius: theme.palette.borderRadius, }} onChange={(event, newValue) => { - console.log("CLICK: ", newValue) - //handleWorkflowSelectionUpdate({ target: { value: newValue} }) - //setSelectedWorkflows= - //var newvalue = [] - //for (var key in newValue) { - // if (newValue[key].id !== undefined) { - // newvalue.push(newValue[key].id) - // } - //} + console.log("CLICK: ", newValue) + //handleWorkflowSelectionUpdate({ target: { value: newValue} }) + //setSelectedWorkflows= + //var newvalue = [] + //for (var key in newValue) { + // if (newValue[key].id !== undefined) { + // newvalue.push(newValue[key].id) + // } + //} - // Doing this way as you may want to remove some too - for (var key in workflows) { - if (!newValue.find(data => data.id === workflows[key].id)) { - // Check if it has the one in it - if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) { - const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name) - if (filtered !== undefined && filtered !== null) { - //console.log("Removing: ", workflows[key].name, workflows[key]) - workflows[key]["usecase_ids"] = filtered - - setWorkflow(workflows[key]) - } - } + // Doing this way as you may want to remove some too + for (var key in workflows) { + if (!newValue.find(data => data.id === workflows[key].id)) { + // Check if it has the one in it + if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) { + const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name) + if (filtered !== undefined && filtered !== null) { + //console.log("Removing: ", workflows[key].name, workflows[key]) + workflows[key]["usecase_ids"] = filtered + + setWorkflow(workflows[key]) + } + } - continue - } + continue + } - if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) { - workflows[key]["usecase_ids"] = [subcase.name] - console.log("Setting: ", workflows[key].name) - setWorkflow(workflows[key]) + if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) { + workflows[key]["usecase_ids"] = [subcase.name] + console.log("Setting: ", workflows[key].name) + setWorkflow(workflows[key]) - } else if (!workflows[key]["usecase_ids"].includes(subcase.name)) { - workflows[key]["usecase_ids"].push(subcase.name) - console.log("Adding: ", workflows[key].name) - setWorkflow(workflows[key]) + } else if (!workflows[key]["usecase_ids"].includes(subcase.name)) { + workflows[key]["usecase_ids"].push(subcase.name) + console.log("Adding: ", workflows[key].name) + setWorkflow(workflows[key]) - } - } + } + } - setWorkflows(workflows) - console.log("New: ", newValue) - setSelectedWorkflows(newValue) + setWorkflows(workflows) + console.log("New: ", newValue) + setSelectedWorkflows(newValue) //setUpdate(Math.random()) }} - renderOption={(props, option) => { - //console.log("In options?: ", props, option) + renderOption={(props, data, state) => { + var newname = data.name + if (newname === undefined || newname === null) { + newname = "placeholder" + } - var newname = props.name - if (newname === undefined || newname === null) { - newname = "placeholder" - } - - if (newname.length > 2) { - newname = newname.charAt(0).toUpperCase() + newname.substring(1) - } - return ( -
  • - - {props.image !== undefined && props.image !== null && props.image.length > 0 ? - {newname} - : null} - - Choose {newname} - - - } placement="bottom"> - - } - checkedIcon={} - style={{ marginRight: 8 }} - checked={option.selected} - /> - {newname} - - -
  • - ) - }} + if (newname.length > 2) { + newname = newname.charAt(0).toUpperCase() + newname.substring(1) + } + return ( +
  • + + {data.image !== undefined && data.image !== null && data.image.length > 0 ? + {newname} + : null} + + Choose {newname} + + + } placement="bottom"> + + } + checkedIcon={} + style={{ marginRight: 8 }} + checked={selectedWorkflows.find(wf => wf.id === data.id) !== undefined} + /> + {newname} + + +
  • + ) + }} renderInput={(params) => { return ( - ); }} /> - : null} + : null} + + {}}> + Try it out + + + + {/* - {/*subcase.matches.length > 0 ? - - {subcase.matches.map((workflow, workflowindex) => { - return ( - - - - ) - })} - - : -
    - - No workflow selected yet. - -
    - */} {subcase.extra_buttons !== undefined && subcase.extra_buttons !== null && subcase.extra_buttons.length > 0 ?
    @@ -852,15 +916,9 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged {}}> See other Public Workflows for {} - {/* -
    - - No workflows yet. - -
    - */}
    + */}
    }
    { paddingBottom: "30px", paddingTop: "30px", position: "relative", - backgroundColor: theme.palette.inputColor, textAlign: "left", marginTop: 15, }} @@ -331,17 +330,25 @@ const LoginDialog = (props) => { variant="body2" style={{ marginBottom: 20, color: "white" }} > - 1. Make sure shuffle-database folder has correct access:{" "} + 1. Make sure shuffle-database folder has correct access, and that you have a minimum of 2Gb of RAM available:{" "}

    sudo chown 1000:1000 -R shuffle-database - - 2. Restart docker-compose: + 2. Disable memory swap on the host +
    +
    + sudo swapoff -a +
    + + 3. Restart docker-compose:

    sudo docker-compose restart @@ -513,7 +520,7 @@ const LoginDialog = (props) => { //id="sso_button" const ssoBtn = document.getElementById("sso_button"); if (ssoBtn !== undefined && ssoBtn !== null) { - console.log("SSO BTN: ", ssoBtn) + //console.log("SSO BTN: ", ssoBtn) const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; var tmpView = new URLSearchParams(cursearch).get("autologin"); if (tmpView !== undefined && tmpView !== null) { @@ -527,7 +534,7 @@ const LoginDialog = (props) => { }, 200); }, [ssoUrl]) - return
    {loadedCheck}
    ; + return
    {loadedCheck}
    ; }; export default LoginDialog; diff --git a/frontend/src/views/UpdateAuthentication.jsx b/frontend/src/views/UpdateAuthentication.jsx index a0d0f485..054244c3 100644 --- a/frontend/src/views/UpdateAuthentication.jsx +++ b/frontend/src/views/UpdateAuthentication.jsx @@ -26,7 +26,7 @@ const SetAuthentication = (props) => { //const alert = useAlert(); const parseIncomingOpenapiData = (data) => { - if (data.app === undefined || data.app === null) { + if (data.app === undefined || data.app === null) { return } @@ -43,11 +43,11 @@ const SetAuthentication = (props) => { parsedapp.name = parsedapp.name.replaceAll("_", " "); setApp(parsedapp); - document.title = parsedapp.name + " App Auth"; + document.title = parsedapp.name + " App Auth"; } - console.log("App: ", app) + console.log("App: ", app) const getApp = (appid) => { if (serverside === true) { @@ -64,21 +64,21 @@ const SetAuthentication = (props) => { }) .then((response) => { if (response.status !== 200) { - if (isCloud) { - ReactGA.event({ - category: "appauth", - action: `app_not_found`, - label: appid, - }); - } + if (isCloud) { + ReactGA.event({ + category: "appauth", + action: `app_not_found`, + label: appid, + }); + } } else { - if (isCloud) { - ReactGA.event({ - category: "appauth", - action: `app_found`, - label: appid, - }); - } + if (isCloud) { + ReactGA.event({ + category: "appauth", + action: `app_found`, + label: appid, + }); + } } return response.json(); diff --git a/frontend/src/views/Welcome.jsx b/frontend/src/views/Welcome.jsx index 749aa95b..eed2ad8f 100644 --- a/frontend/src/views/Welcome.jsx +++ b/frontend/src/views/Welcome.jsx @@ -23,6 +23,7 @@ import { } from '@mui/material'; import theme from '../theme.jsx'; import { useNavigate, Link } from "react-router-dom"; +import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; import Drift from "react-driftjs"; const Welcome = (props) => { @@ -301,44 +302,47 @@ const Welcome = (props) => { flex: 1, padding: 0, textAlign: "center", - maxWidth: 300, - minWidth: 300, + maxWidth: 275, + minWidth: 275, backgroundColor: theme.palette.surfaceColor, color: "white", borderRadius: theme.palette.borderRadius, } const actionObject = { - padding: "35px", - maxHeight: 300, - minHeight: 300, + padding: "25px", + maxHeight: 280, + minHeight: 280, borderRadius: theme.palette.borderRadius, } const imageStyle = { - width: 150, + width: 70, // height: 150, // margin: "auto", // marginTop: 10, - borderRadius: 75, + marginBottom: 18, + // borderRadius: 75, objectFit: "scale-down", } const buttonStyle = { - borderRadius: 8, + borderRadius: 200, height: 51, width: 464, fontSize: 16, - background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", + // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", + background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", padding: "16px 24px", - top: 75, + top: 105, margin: "auto", itemAlign: "center", + marginLeft: "65px", } const defaultImage = "/images/experienced.png" const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage return ( -
    +
    {/*
    @@ -346,7 +350,7 @@ const Welcome = (props) => { */} {showWelcome === true ?
    -
    + {/*
    { ) })} -
    - +
    */} +
    {/* @@ -414,7 +418,7 @@ const Welcome = (props) => { />
    - {frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null : + {/* {frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null :
    App Framework @@ -439,32 +443,35 @@ const Welcome = (props) => { />
    - } + } */}
    : -
    - {/* -
    - { - navigate("/login") +
    + + {/*
    + { + navigate("/workflows") }}/> - { - navigate("/login") + { + navigate("/workflows") }}> Back -
    - */} - +
    */} + + Help us get to know you - - We will use this information to personalize your automation + + Let us help you create a smoother journey. + {/* + We will use this information to personalize your automation + */}
    -
    +
    { if (isCloud) { ReactGA.event({ @@ -506,7 +513,7 @@ const Welcome = (props) => { navigate("/workflows?message=Skipped intro") }}> - + Experienced @@ -517,17 +524,22 @@ const Welcome = (props) => {
    - {/* -
    + +
    - */} + -
    { + + {/*
    { if (window.drift !== undefined) { window.drift.api.startInteraction({ interactionId: 340045 }) } else { @@ -537,7 +549,7 @@ const Welcome = (props) => { Want a demo instead? -
    +
    */}
    } diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 476dd9fc..59ffd078 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -11,13 +11,13 @@ import { isMobile } from "react-device-detect" import { Badge, - Divider, + Divider, Avatar, Drawer, Grid, - InputLabel, - Select, - ListSubheader, + InputLabel, + Select, + ListSubheader, Paper, Tooltip, Button, @@ -533,7 +533,6 @@ const Workflows = (props) => { document.title = "Shuffle - Workflows"; let navigate = useNavigate(); - //const alert = useAlert(); const classes = useStyles(theme); const imgSize = 60; @@ -590,6 +589,9 @@ const Workflows = (props) => { const [drawerOpen, setDrawerOpen] = React.useState(false) const [videoViewOpen, setVideoViewOpen] = React.useState(false) const [gettingStartedItems, setGettingStartedItems] = React.useState([]) + const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([]) + + const [apps, setApps] = React.useState([]); const drawerWidth = drawerOpen ? 325 : 0 @@ -725,6 +727,30 @@ const Workflows = (props) => { } }; + 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) => { + setApps(responseJson); + }) + .catch((error) => { + console.log("App loading error: "+error.toString()); + }); + } + const addFilter = (data) => { if (data === null || data === undefined) { console.log("No filter data") @@ -891,13 +917,15 @@ const Workflows = (props) => { backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: 500, + padding: 50, }, }} >
    - Are you sure you want to delete this workflow?
    - Other workflows relying on this one may stop working + Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}?
    + + Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
    { setTimeout(() => { getAvailableWorkflows(); }, 1000); - } + } else if (selectedWorkflowIndexes.length > 0) { + // Do backwards so it doesn't change + for (var i = selectedWorkflowIndexes.length - 1; i >= 0; i--) { + const workflow = filteredWorkflows[selectedWorkflowIndexes[i]-1] + if (workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null) { + deleteWorkflow(workflow.id); + } + } + + setTimeout(() => { + getAvailableWorkflows(); + }, 1000); + + setSelectedWorkflowIndexes([]); + } setDeleteModalOpen(false); }} color="primary" @@ -1115,9 +1157,9 @@ const Workflows = (props) => { // Ensures the zooming happens only once per load setTimeout(() => { - fetchUsecases(newarray) - setFirstLoad(false) - }, 100) + fetchUsecases(newarray) + setFirstLoad(false) + }, 100) } else { if (isLoggedIn) { @@ -1223,8 +1265,9 @@ const Workflows = (props) => { setView(tmpView); } + getApps() getAvailableWorkflows(); - getFramework() + getFramework() } }, []) @@ -1280,6 +1323,7 @@ const Workflows = (props) => { color: "white", margin: "10px", backgroundColor: theme.palette.surfaceColor, + position: "relative", }; const workflowActionStyle = { @@ -1708,21 +1752,22 @@ const Workflows = (props) => { { - event.stopPropagation() - ReactDOM.unstable_batchedUpdates(() => { - setModalOpen(true); - setEditingWorkflow(JSON.parse(JSON.stringify(data))); - setNewWorkflowName(data.name); - setNewWorkflowDescription(data.description); - setDefaultReturnValue(data.default_return_value); - if (data.tags !== undefined && data.tags !== null) { - setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))); - } + event.stopPropagation() + ReactDOM.unstable_batchedUpdates(() => { + setIsEditing(true) + setModalOpen(true); + setEditingWorkflow(JSON.parse(JSON.stringify(data))); + setNewWorkflowName(data.name); + setNewWorkflowDescription(data.description); + setDefaultReturnValue(data.default_return_value); + if (data.tags !== undefined && data.tags !== null) { + setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))); + } - if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) { - setSelectedUsecases(data.usecase_ids) - } - }) + if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) { + setSelectedUsecases(data.usecase_ids) + } + }) }} key={"change"} > @@ -2654,6 +2699,7 @@ const Workflows = (props) => { renderCell: (params) => {}, }, ]; + let rows = []; rows = filteredWorkflows.map((data, index) => { let obj = { @@ -2671,17 +2717,55 @@ const Workflows = (props) => { className={classes.datagrid} rows={rows} columns={columns} - pageSize={20} + pageSize={25} checkboxSelection autoHeight density="standard" + onSelectionModelChange={(newSelection) => { + //setSelectedWorkflows(newSelection.selectionModel); + console.log(newSelection) + + setSelectedWorkflowIndexes(newSelection) + }} + selectionModel={selectedWorkflowIndexes} components={{ Toolbar: GridToolbar, }} /> ); } - return
    {workflowData}
    ; + return ( +
    + + { + setModalOpen(true) + setIsEditing(false) + }} + > + + + + {filteredWorkflows.length === 0 ? null : + { + setDeleteModalOpen(true) + }} + > + + + + + } + {workflowData} +
    + ) }; var total_count = 0 @@ -3152,70 +3236,20 @@ const Workflows = (props) => { const WorkflowView = () => { if (workflows.length === 0) { - // Not going there yet - //if ((userdata.tutorials !== undefined && userdata.tutorials !== null && !userdata.tutorials.includes("getting-started")) || userdata.tutorials === null) { - // return ; - //} - //return ( - //
    - // - //
    - //

    Welcome to Shuffle

    - //
    - //
    - //

    - // Shuffle is a flexible, easy to use, automation platform - // allowing users to integrate their services and devices freely. - // It's made to significantly reduce the amount of manual labor, - // and is focused on security applications.{" "} - // - // Click here to learn more. - // - //

    - //
    - //
    - // If you want to jump straight into it, click here to create your - // first workflow: - //
    - //
    - // - // - // - // ..OR - // - // {workflowButtons} - // - //
    - //
    - //
    - //) } - var workflowDelay = -150 - var appDelay = -75 + var workflowDelay = -150 + var appDelay = -75 - const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) + const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) return (
    - - Workflows - + + Workflows +
    {/*
    @@ -3243,6 +3277,7 @@ const Workflows = (props) => { minWidth: 275, }, }} + rows={1} placeholder="Filter Workflows" color="primary" fullWidth @@ -3268,53 +3303,6 @@ const Workflows = (props) => { {workflowButtons}
    - {/* -
    -
    -
    -
    -
    -
    {workflows.length}
    -
    ACTIVE WORKFLOWS
    -
    -
    -
    -
    -
    -
    -
    -
    {workflows.length}
    -
    AVAILABE WORKFLOWS
    -
    -
    -
    -
    -
    -
    -
    -
    {workflows.length}
    -
    NOTIFICATIONS
    -
    -
    -
    -
    - */} - - {/* - chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => { - console.log("VALUE: ", value) - - return ( - - {value} - - ) - }} - */}
    {!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ? @@ -3772,12 +3760,12 @@ const Workflows = (props) => { } const gettingStartedDrawer = - { {modalOpen === true ? { isEditing={isEditing} workflows={workflows} + apps={apps} setWorkflows={setWorkflows} /> : null} diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh index a379f69c..1c58cfda 100755 --- a/functions/onprem/worker/build.sh +++ b/functions/onprem/worker/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-worker -VERSION=1.2.1 +VERSION=1.2.2 echo "Running docker build with $NAME:$VERSION" #CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin . @@ -11,10 +11,10 @@ docker build . -t frikky/shuffle:$NAME -t frikky/shuffle:$NAME_$VERSION -t docke #docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION #docker tag frikky/shuffle:0.8.51 ghcr.io/frikky/shuffle-worker:0.8.5 #docker tag frikky/shuffle:$NAME ghcr.io/frikky/shuffle-worker:0.8.52 -docker push frikky/shuffle:$NAME -docker push ghcr.io/frikky/$NAME:$VERSION -docker push ghcr.io/frikky/$NAME:nightly - -docker push shuffle/shuffle:$NAME -docker push ghcr.io/shuffle/$NAME:$VERSION -docker push ghcr.io/shuffle/$NAME:nightly +#docker push frikky/shuffle:$NAME +#docker push ghcr.io/frikky/$NAME:$VERSION +#docker push ghcr.io/frikky/$NAME:nightly +# +#docker push shuffle/shuffle:$NAME +#docker push ghcr.io/shuffle/$NAME:$VERSION +#docker push ghcr.io/shuffle/$NAME:nightly diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index f6b1406e..3916e197 100755 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -11,7 +11,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.4.17 + github.com/shuffle/shuffle-shared v0.4.50 ) require ( @@ -47,16 +47,18 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opensearch-project/opensearch-go v1.1.0 // indirect + github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect go.opencensus.io v0.24.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/crypto v0.1.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/tools v0.1.12 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.106.0 // indirect diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum index 6cfb1be4..23d2ba9e 100755 --- a/functions/onprem/worker/go.sum +++ b/functions/onprem/worker/go.sum @@ -60,17 +60,34 @@ github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ= github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg= github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= +github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4= +github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw= github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -86,6 +103,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM= github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ= @@ -144,6 +162,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= @@ -155,6 +174,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -181,6 +201,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -201,6 +223,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= +github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo= +github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= +github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -222,6 +248,8 @@ github.com/shuffle/shuffle-shared v0.4.9 h1:mGCaLcSbrsQCy26pJXPlZAtitEzEEwqotGNj github.com/shuffle/shuffle-shared v0.4.9/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ= github.com/shuffle/shuffle-shared v0.4.17 h1:56ll366bdmIJu/7GFqNC2XTjjl0SGBf430PSq+EB6Ro= github.com/shuffle/shuffle-shared v0.4.17/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ= +github.com/shuffle/shuffle-shared v0.4.50 h1:fJLfhWIJ5mYap4JwHnD/B5aaLyIULwylFSl3FoWlajM= +github.com/shuffle/shuffle-shared v0.4.50/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -235,10 +263,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -256,6 +286,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -322,8 +354,14 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -344,6 +382,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -375,17 +414,31 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= From acd29076a857012cf496197a712cd472c30c3fd9 Mon Sep 17 00:00:00 2001 From: Dhaval Dave Date: Tue, 17 Oct 2023 17:38:31 +0530 Subject: [PATCH 093/160] added all in one file for deployment --- functions/kubernetes/all-in-one.yaml | 813 +++++++++++++++++++++++++++ 1 file changed, 813 insertions(+) create mode 100644 functions/kubernetes/all-in-one.yaml diff --git a/functions/kubernetes/all-in-one.yaml b/functions/kubernetes/all-in-one.yaml new file mode 100644 index 00000000..db591402 --- /dev/null +++ b/functions/kubernetes/all-in-one.yaml @@ -0,0 +1,813 @@ +--- + +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: shuffle-data +provisioner: kubernetes.io/no-provisioner +volumeBindingMode: WaitForFirstConsumer + +--- + +apiVersion: v1 +data: + BACKEND_HOSTNAME: shuffle-backend + BACKEND_PORT: "5001" + BASE_URL: http://shuffle-backend:5001 + DATASTORE_EMULATOR_HOST: shuffle-database:8000 + DB_LOCATION: /mnt/shuffle-data/open-search + DOCKER_API_VERSION: "1.40" + ENVIRONMENT_NAME: Shuffle + FRONTEND_PORT: "3001" + FRONTEND_PORT_HTTPS: "3443" + HTTP_PROXY: "" + HTTPS_PROXY: "" + ORBORUS_CONTAINER_NAME: "\t\t\t\t" + ORG_ID: Shuffle + OUTER_HOSTNAME: shuffle-backend + SHUFFLE_APP_DOWNLOAD_LOCATION: https://github.com/shuffle/python-apps + SHUFFLE_APP_FORCE_UPDATE: "false" + SHUFFLE_APP_HOTLOAD_FOLDER: /shuffle-apps + SHUFFLE_APP_HOTLOAD_LOCATION: ./shuffle-apps + SHUFFLE_BASE_IMAGE_NAME: shuffle + SHUFFLE_BASE_IMAGE_REGISTRY: ghcr.io + SHUFFLE_BASE_IMAGE_TAG_SUFFIX: -1.0.0 + SHUFFLE_CHAT_DISABLED: "false" + SHUFFLE_CONTAINER_AUTO_CLEANUP: "false" + SHUFFLE_DEFAULT_APIKEY: "" + SHUFFLE_DEFAULT_PASSWORD: "" + SHUFFLE_DEFAULT_USERNAME: "" + SHUFFLE_DOWNLOAD_AUTH_BRANCH: "" + SHUFFLE_DOWNLOAD_AUTH_PASSWORD: "" + SHUFFLE_DOWNLOAD_AUTH_USERNAME: "" + SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH: "" + SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION: "" + SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD: "" + SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME: "" + SHUFFLE_ELASTIC: "true" + SHUFFLE_ENCRYPTION_MODIFIER: "" + SHUFFLE_FILE_LOCATION: /shuffle-files + SHUFFLE_LOGS_DISABLED: "false" + SHUFFLE_OPENSEARCH_APIKEY: "" + SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: "" + SHUFFLE_OPENSEARCH_CLOUDID: "" + SHUFFLE_OPENSEARCH_INDEX_PREFIX: "" + SHUFFLE_OPENSEARCH_PASSWORD: admin + SHUFFLE_OPENSEARCH_PROXY: "" + SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: "true" + SHUFFLE_OPENSEARCH_URL: https://opensearch:9200 + SHUFFLE_OPENSEARCH_USERNAME: admin + SHUFFLE_ORBORUS_STARTUP_DELAY: "\t\t" + SHUFFLE_PASS_APP_PROXY: "FALSE" + SHUFFLE_PASS_WORKER_PROXY: "TRUE" + SHUFFLE_RERUN_SCHEDULE: "300" + SSO_REDIRECT_URL: "" + TZ: "Europe/Amsterdam \t\t\t\t\t" + IS_KUBERNETES: "true" + REGISTRY_URL: "172.17.14.71:5000" + REGISTRY_AUTH: "false" + SHUFFLE_KUBERNETES_WORKER: "dhavald055/shuffle-k8s:shuffle-worker-kubernetes" +kind: ConfigMap +metadata: + creationTimestamp: null + labels: + io.kompose.service: backend-env + name: env + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: shuffle + name: pod-manager +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "create", "update", "delete"] +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["create", "get", "list", "watch", "delete"] + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pod-manager-binding + namespace: shuffle +subjects: +- kind: ServiceAccount + name: default + namespace: shuffle +roleRef: + kind: Role + name: pod-manager + apiGroup: rbac.authorization.k8s.io + +--- + +apiVersion: v1 +kind: PersistentVolume +metadata: + name: shuffle-os-pv +spec: + capacity: + storage: 10Gi # Adjust the storage size as per your requirements + accessModes: + - ReadWriteOnce # This allows read-write access to a single node + persistentVolumeReclaimPolicy: Retain # Adjust the reclaim policy as per your needs + storageClassName: shuffle-data # Set the desired storage class + hostPath: + path: /mnt/shuffle-data/open-search + +--- + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: opensearch-claim0 + name: opensearch-claim0 +spec: + accessModes: + - ReadWriteOnce + storageClassName: shuffle-data + resources: + requests: + storage: 500Mi +status: {} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: opensearch + name: opensearch +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: opensearch + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: opensearch + spec: + # securityContext: + # runAsUser: 1000 # UID + # fsGroup: 1000 # GID + # nodeSelector: + # node: worker1 + initContainers: + - name: volume-permissions + image: busybox + command: ["sh", "-c", "chown -R 1000:1000 /usr/share/opensearch/data"] + volumeMounts: + - name: opensearch-claim0 + mountPath: /usr/share/opensearch/data + containers: + - env: + - name: OPENSEARCH_JAVA_OPTS + value: -Xms1024m -Xmx1024m + #- name: bootstrap.memory_lock + #value: "true" + - name: cluster.initial_master_nodes + value: shuffle-opensearch + - name: cluster.name + value: shuffle-cluster + - name: cluster.routing.allocation.disk.threshold_enabled + value: "false" + - name: discovery.seed_hosts + value: shuffle-opensearch + - name: node.name + value: shuffle-opensearch + - name: node.store.allow_mmap + value: "false" + - name: DB_LOCATION + valueFrom: + configMapKeyRef: + name: env + key: DB_LOCATION + image: opensearchproject/opensearch:2.5.0 + name: shuffle-opensearch + ports: + - containerPort: 9200 + resources: {} + volumeMounts: + - mountPath: /usr/share/opensearch/data + name: opensearch-claim0 + hostname: shuffle-opensearch + restartPolicy: Always + volumes: + - name: opensearch-claim0 + persistentVolumeClaim: + claimName: opensearch-claim0 +status: {} + +--- + +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: opensearch + name: opensearch +spec: + type: NodePort + ports: + - name: "9200" + port: 9200 + targetPort: 9200 + nodePort: 31001 + selector: + io.kompose.service: opensearch +status: + loadBalancer: {} + + +--- + +apiVersion: v1 +kind: PersistentVolume +metadata: + name: shuffle-apps-pv +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: shuffle-data + hostPath: + path: /mnt/shuffle-data/backend + +## --- + +# apiVersion: v1 +# kind: PersistentVolume +# metadata: +# name: shuffle-files-pv +# spec: +# capacity: +# storage: 5Gi +# accessModes: +# - ReadWriteOnce +# persistentVolumeReclaimPolicy: Retain +# storageClassName: fast +# hostPath: +# path: /mnt/shuffle-data/backend + +# --- + +# apiVersion: v1 +# kind: PersistentVolumeClaim +# metadata: +# creationTimestamp: null +# labels: +# io.kompose.service: backend-files-claim +# name: backend-files-claim +# spec: +# accessModes: +# - ReadWriteOnce +# storageClassName: fast +# resources: +# requests: +# storage: 1Gi +# status: {} + +--- + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: backend-apps-claim + name: backend-apps-claim +spec: + accessModes: + - ReadWriteOnce + storageClassName: shuffle-data + resources: + requests: + storage: 1Gi +# status: {} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: backend + name: backend +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: backend + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: backend + app: shuffle-backend + name: shuffle-backend + spec: + volumes: + # - name: shuffle-files + # persistentVolumeClaim: + # claimName: backend-files-claim + # - name: shuffle-apps + # hostPath: + # path: /mnt/shuffle-data/backend + # type: DirectoryOrCreate + - name: shuffle-apps + persistentVolumeClaim: + claimName: backend-apps-claim + # - name: docker-socket + # hostPath: + # path: /var/run/docker.sock +# nodeSelector: +# node: master + containers: + - env: + - name: BACKEND_HOSTNAME + valueFrom: + configMapKeyRef: + key: BACKEND_HOSTNAME + name: env + - name: BACKEND_PORT + valueFrom: + configMapKeyRef: + key: BACKEND_PORT + name: env + - name: BASE_URL + valueFrom: + configMapKeyRef: + key: BASE_URL + name: env + - name: DATASTORE_EMULATOR_HOST + valueFrom: + configMapKeyRef: + key: DATASTORE_EMULATOR_HOST + name: env + - name: DB_LOCATION + valueFrom: + configMapKeyRef: + key: DB_LOCATION + name: env + - name: DOCKER_API_VERSION + valueFrom: + configMapKeyRef: + key: DOCKER_API_VERSION + name: env + - name: ENVIRONMENT_NAME + valueFrom: + configMapKeyRef: + key: ENVIRONMENT_NAME + name: env + - name: FRONTEND_PORT + valueFrom: + configMapKeyRef: + key: FRONTEND_PORT + name: env + - name: FRONTEND_PORT_HTTPS + valueFrom: + configMapKeyRef: + key: FRONTEND_PORT_HTTPS + name: env + - name: HTTPS_PROXY + valueFrom: + configMapKeyRef: + key: HTTPS_PROXY + name: env + - name: HTTP_PROXY + valueFrom: + configMapKeyRef: + key: HTTP_PROXY + name: env + - name: ORBORUS_CONTAINER_NAME + valueFrom: + configMapKeyRef: + key: ORBORUS_CONTAINER_NAME + name: env + - name: ORG_ID + valueFrom: + configMapKeyRef: + key: ORG_ID + name: env + - name: OUTER_HOSTNAME + valueFrom: + configMapKeyRef: + key: OUTER_HOSTNAME + name: env + - name: SHUFFLE_APP_DOWNLOAD_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_DOWNLOAD_LOCATION + name: env + - name: SHUFFLE_APP_FORCE_UPDATE + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_FORCE_UPDATE + name: env + - name: SHUFFLE_APP_HOTLOAD_FOLDER + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_HOTLOAD_FOLDER + name: env + - name: SHUFFLE_APP_HOTLOAD_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_APP_HOTLOAD_LOCATION + name: env + - name: SHUFFLE_BASE_IMAGE_NAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_BASE_IMAGE_NAME + name: env + - name: SHUFFLE_BASE_IMAGE_REGISTRY + valueFrom: + configMapKeyRef: + key: SHUFFLE_BASE_IMAGE_REGISTRY + name: env + - name: SHUFFLE_BASE_IMAGE_TAG_SUFFIX + valueFrom: + configMapKeyRef: + key: SHUFFLE_BASE_IMAGE_TAG_SUFFIX + name: env + - name: SHUFFLE_CHAT_DISABLED + valueFrom: + configMapKeyRef: + key: SHUFFLE_CHAT_DISABLED + name: env + - name: SHUFFLE_CONTAINER_AUTO_CLEANUP + valueFrom: + configMapKeyRef: + key: SHUFFLE_CONTAINER_AUTO_CLEANUP + name: env + - name: SHUFFLE_DEFAULT_APIKEY + valueFrom: + configMapKeyRef: + key: SHUFFLE_DEFAULT_APIKEY + name: env + - name: SHUFFLE_DEFAULT_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_DEFAULT_PASSWORD + name: env + - name: SHUFFLE_DEFAULT_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_DEFAULT_USERNAME + name: env + - name: SHUFFLE_DOWNLOAD_AUTH_BRANCH + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_AUTH_BRANCH + name: env + - name: SHUFFLE_DOWNLOAD_AUTH_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_AUTH_PASSWORD + name: env + - name: SHUFFLE_DOWNLOAD_AUTH_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_AUTH_USERNAME + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD + name: env + - name: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME + name: env + - name: SHUFFLE_ELASTIC + valueFrom: + configMapKeyRef: + key: SHUFFLE_ELASTIC + name: env + - name: SHUFFLE_ENCRYPTION_MODIFIER + valueFrom: + configMapKeyRef: + key: SHUFFLE_ENCRYPTION_MODIFIER + name: env + - name: SHUFFLE_FILE_LOCATION + valueFrom: + configMapKeyRef: + key: SHUFFLE_FILE_LOCATION + name: env + - name: SHUFFLE_LOGS_DISABLED + valueFrom: + configMapKeyRef: + key: SHUFFLE_LOGS_DISABLED + name: env + - name: SHUFFLE_OPENSEARCH_APIKEY + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_APIKEY + name: env + - name: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE + name: env + - name: SHUFFLE_OPENSEARCH_CLOUDID + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_CLOUDID + name: env + - name: SHUFFLE_OPENSEARCH_INDEX_PREFIX + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_INDEX_PREFIX + name: env + - name: SHUFFLE_OPENSEARCH_PASSWORD + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_PASSWORD + name: env + - name: SHUFFLE_OPENSEARCH_PROXY + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_PROXY + name: env + - name: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY + name: env + - name: SHUFFLE_OPENSEARCH_URL + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_URL + name: env + - name: SHUFFLE_OPENSEARCH_USERNAME + valueFrom: + configMapKeyRef: + key: SHUFFLE_OPENSEARCH_USERNAME + name: env + - name: SHUFFLE_ORBORUS_STARTUP_DELAY + valueFrom: + configMapKeyRef: + key: SHUFFLE_ORBORUS_STARTUP_DELAY + name: env + - name: SHUFFLE_PASS_APP_PROXY + valueFrom: + configMapKeyRef: + key: SHUFFLE_PASS_APP_PROXY + name: env + - name: SHUFFLE_PASS_WORKER_PROXY + valueFrom: + configMapKeyRef: + key: SHUFFLE_PASS_WORKER_PROXY + name: env + - name: SHUFFLE_RERUN_SCHEDULE + valueFrom: + configMapKeyRef: + key: SHUFFLE_RERUN_SCHEDULE + name: env + - name: SSO_REDIRECT_URL + valueFrom: + configMapKeyRef: + key: SSO_REDIRECT_URL + name: env + - name: TZ + valueFrom: + configMapKeyRef: + key: TZ + name: env + - name: IS_KUBERNETES + valueFrom: + configMapKeyRef: + key: IS_KUBERNETES + name: env + - name: REGISTRY_URL + valueFrom: + configMapKeyRef: + key: REGISTRY_URL + name: env + - name: REGISTRY_AUTH + valueFrom: + configMapKeyRef: + key: REGISTRY_AUTH + name: env + image: dhavald055/shuffle-k8s:shuffle-backend-kubernetes + imagePullPolicy: Always + name: shuffle-backend + ports: + - containerPort: 5001 + resources: {} + volumeMounts: + # - name: docker-socket + # mountPath: /var/run/docker.sock + - name: shuffle-apps + mountPath: /app/generated + # - name: shuffle-files + # mountPath: /shuffle-files + restartPolicy: Always +status: {} + +--- + +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: backend + name: shuffle-backend +spec: + type: NodePort + ports: + - name: "50001" + port: 5001 + targetPort: 5001 + nodePort: 30009 + selector: + io.kompose.service: backend +status: + loadBalancer: {} + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: frontend + name: frontend +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: frontend + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: frontend + spec: + containers: + - env: + - name: BACKEND_HOSTNAME + image: ghcr.io/shuffle/shuffle-frontend:latest + name: shuffle-frontend + ports: + - containerPort: 80 + - containerPort: 443 + resources: {} + hostname: shuffle-frontend + restartPolicy: Always +status: {} + +--- + +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: frontend + name: frontend +spec: + type: NodePort + ports: + - name: "80" + port: 80 + targetPort: 80 + nodePort: 30007 + - name: "443" + port: 443 + targetPort: 443 + nodePort: 30008 + selector: + io.kompose.service: frontend +# status: +# loadBalancer: {} + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.service: orborus + name: orborus +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: orborus + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.0 (40646f47) + creationTimestamp: null + labels: + io.kompose.network/shuffle: "true" + io.kompose.service: orborus + spec: + containers: + - env: + - name: BASE_URL + value: "http://shuffle-backend:5001" + - name: DOCKER_API_VERSION + value: "1.40" + - name: ENVIRONMENT_NAME + value: Shuffle + - name: ORG_ID + value: Shuffle + - name: SHUFFLE_APP_SDK_VERSION + value: nightly + - name: SHUFFLE_SCALE_REPLICAS + value: "5" + #- name: SHUFFLE_SWARM_CONFIG + #value: run + - name: SHUFFLE_WORKER_VERSION + value: nightly + - name: IS_KUBERNETES + valueFrom: + configMapKeyRef: + key: IS_KUBERNETES + name: env + - name: REGISTRY_URL + valueFrom: + configMapKeyRef: + key: REGISTRY_URL + name: env + - name: SHUFFLE_KUBERNETES_WORKER + valueFrom: + configMapKeyRef: + key: SHUFFLE_KUBERNETES_WORKER + name: env + + image: dhavald055/shuffle-k8s:shuffle-orborus-kubernetes + #imagePullPolicy: Never + name: shuffle-orborus + resources: {} + hostname: shuffle-orborus + restartPolicy: Always +status: {} From 11c176f56dc5ae718449cd8f0d324d53d01592d6 Mon Sep 17 00:00:00 2001 From: dhaval055 Date: Wed, 18 Oct 2023 12:46:49 +0000 Subject: [PATCH 094/160] fixed all-in-one.yaml --- functions/kubernetes/all-in-one.yaml | 92 ++++++++++++---------------- 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/functions/kubernetes/all-in-one.yaml b/functions/kubernetes/all-in-one.yaml index db591402..be83725b 100644 --- a/functions/kubernetes/all-in-one.yaml +++ b/functions/kubernetes/all-in-one.yaml @@ -230,18 +230,15 @@ metadata: io.kompose.service: opensearch name: opensearch spec: - type: NodePort ports: - name: "9200" port: 9200 targetPort: 9200 - nodePort: 31001 selector: io.kompose.service: opensearch status: loadBalancer: {} - --- apiVersion: v1 @@ -250,7 +247,7 @@ metadata: name: shuffle-apps-pv spec: capacity: - storage: 10Gi + storage: 5Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain @@ -258,39 +255,38 @@ spec: hostPath: path: /mnt/shuffle-data/backend -## --- +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: shuffle-files-pv +spec: + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: shuffle-data + hostPath: + path: /mnt/shuffle-data/backend -# apiVersion: v1 -# kind: PersistentVolume -# metadata: -# name: shuffle-files-pv -# spec: -# capacity: -# storage: 5Gi -# accessModes: -# - ReadWriteOnce -# persistentVolumeReclaimPolicy: Retain -# storageClassName: fast -# hostPath: -# path: /mnt/shuffle-data/backend +--- -# --- - -# apiVersion: v1 -# kind: PersistentVolumeClaim -# metadata: -# creationTimestamp: null -# labels: -# io.kompose.service: backend-files-claim -# name: backend-files-claim -# spec: -# accessModes: -# - ReadWriteOnce -# storageClassName: fast -# resources: -# requests: -# storage: 1Gi -# status: {} + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + creationTimestamp: null + labels: + io.kompose.service: backend-files-claim + name: backend-files-claim + spec: + accessModes: + - ReadWriteOnce + storageClassName: shuffle-data + resources: + requests: + storage: 5Gi +# status: {} --- @@ -307,10 +303,11 @@ spec: storageClassName: shuffle-data resources: requests: - storage: 1Gi + storage: 5Gi # status: {} --- + apiVersion: apps/v1 kind: Deployment metadata: @@ -341,19 +338,12 @@ spec: name: shuffle-backend spec: volumes: - # - name: shuffle-files - # persistentVolumeClaim: - # claimName: backend-files-claim - # - name: shuffle-apps - # hostPath: - # path: /mnt/shuffle-data/backend - # type: DirectoryOrCreate + - name: shuffle-files + persistentVolumeClaim: + claimName: backend-files-claim - name: shuffle-apps persistentVolumeClaim: claimName: backend-apps-claim - # - name: docker-socket - # hostPath: - # path: /var/run/docker.sock # nodeSelector: # node: master containers: @@ -640,12 +630,10 @@ spec: - containerPort: 5001 resources: {} volumeMounts: - # - name: docker-socket - # mountPath: /var/run/docker.sock - name: shuffle-apps mountPath: /app/generated - # - name: shuffle-files - # mountPath: /shuffle-files + - name: shuffle-files + mountPath: /shuffle-files restartPolicy: Always status: {} @@ -662,12 +650,10 @@ metadata: io.kompose.service: backend name: shuffle-backend spec: - type: NodePort ports: - - name: "50001" + - name: "5001" port: 5001 targetPort: 5001 - nodePort: 30009 selector: io.kompose.service: backend status: From 614e39da7034a26f1b01a5b4f66d373c3d870857 Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 18 Oct 2023 14:58:36 +0200 Subject: [PATCH 095/160] Fixed some frontend things for release and orborus is now better by default --- backend/app_sdk/app_base.py | 2 +- backend/go-app/main.go | 103 +------ backend/go-app/walkoff.go | 10 +- docker-compose.yml | 5 +- frontend/src/components/AppFramework.jsx | 10 +- .../src/components/WorkflowTemplatePopup.jsx | 60 +++- frontend/src/views/AppCreator.jsx | 7 +- frontend/src/views/Apps.jsx | 85 +++++- frontend/src/views/Dashboard.jsx | 274 ++++++++++++++---- frontend/src/views/Workflows.jsx | 4 +- functions/onprem/orborus/orborus.go | 17 +- 11 files changed, 378 insertions(+), 199 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index d22bc865..4870d614 100755 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -3519,7 +3519,7 @@ class AppBase: if self.action["app_name"].lower() == "shuffle tools": timeout = 55 - timeout = 30 + #timeout = 30 try: executor = concurrent.futures.ThreadPoolExecutor() diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ad8c63b4..1dac07a9 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -86,59 +86,6 @@ var runningEnvironment = "onprem" var syncUrl = "https://shuffler.io" var dbclient *datastore.Client - -type Userapi struct { - Username string `datastore:"username"` - ApiKey string `datastore:"apikey"` -} - -type ExecutionInfo struct { - TotalApiUsage int64 `json:"total_api_usage" datastore:"total_api_usage"` - TotalWorkflowExecutions int64 `json:"total_workflow_executions" datastore:"total_workflow_executions"` - TotalAppExecutions int64 `json:"total_app_executions" datastore:"total_app_executions"` - TotalCloudExecutions int64 `json:"total_cloud_executions" datastore:"total_cloud_executions"` - TotalOnpremExecutions int64 `json:"total_onprem_executions" datastore:"total_onprem_executions"` - DailyApiUsage int64 `json:"daily_api_usage" datastore:"daily_api_usage"` - DailyWorkflowExecutions int64 `json:"daily_workflow_executions" datastore:"daily_workflow_executions"` - DailyAppExecutions int64 `json:"daily_app_executions" datastore:"daily_app_executions"` - DailyCloudExecutions int64 `json:"daily_cloud_executions" datastore:"daily_cloud_executions"` - DailyOnpremExecutions int64 `json:"daily_onprem_executions" datastore:"daily_onprem_executions"` -} - -// "Execution by status" -// Execution history -//type GlobalStatistics struct { -// BackendExecutions int64 `json:"backend_executions" datastore:"backend_executions"` -// WorkflowCount int64 `json:"workflow_count" datastore:"workflow_count"` -// ExecutionCount int64 `json:"execution_count" datastore:"execution_count"` -// ExecutionSuccessCount int64 `json:"execution_success_count" datastore:"execution_success_count"` -// ExecutionAbortCount int64 `json:"execution_abort_count" datastore:"execution_abort_count"` -// ExecutionFailureCount int64 `json:"execution_failure_count" datastore:"execution_failure_count"` -// ExecutionPendingCount int64 `json:"execution_pending_count" datastore:"execution_pending_count"` -// AppUsageCount int64 `json:"app_usage_count" datastore:"app_usage_count"` -// TotalAppsCount int64 `json:"total_apps_count" datastore:"total_apps_count"` -// SelfMadeAppCount int64 `json:"self_made_app_count" datastore:"self_made_app_count"` -// WebhookUsageCount int64 `json:"webhook_usage_count" datastore:"webhook_usage_count"` -// Baseline map[string]int64 `json:"baseline" datastore:"baseline"` -//} - -type ParsedOpenApi struct { - Body string `datastore:"body,noindex" json:"body"` - ID string `datastore:"id" json:"id"` - Success bool `datastore:"success,omitempty" json:"success,omitempty"` -} - -// Limits set for a user so that they can't do a shitload -type UserLimits struct { - DailyApiUsage int64 `json:"daily_api_usage" datastore:"daily_api_usage"` - DailyWorkflowExecutions int64 `json:"daily_workflow_executions" datastore:"daily_workflow_executions"` - DailyCloudExecutions int64 `json:"daily_cloud_executions" datastore:"daily_cloud_executions"` - DailyTriggers int64 `json:"daily_triggers" datastore:"daily_triggers"` - DailyMailUsage int64 `json:"daily_mail_usage" datastore:"daily_mail_usage"` - MaxTriggers int64 `json:"max_triggers" datastore:"max_triggers"` - MaxWorkflows int64 `json:"max_workflows" datastore:"max_workflows"` -} - type retStruct struct { Success bool `json:"success"` SyncFeatures shuffle.SyncFeatures `json:"sync_features"` @@ -147,43 +94,6 @@ type retStruct struct { Reason string `json:"reason"` } -// Saves some data, not sure what to have here lol -type UserAuth struct { - Description string `json:"description" datastore:"description,noindex" yaml:"description"` - Name string `json:"name" datastore:"name" yaml:"name"` - Workflows []string `json:"workflows" datastore:"workflows"` - Username string `json:"username" datastore:"username"` - Fields []UserAuthField `json:"fields" datastore:"fields"` -} - -type UserAuthField struct { - Key string `json:"key" datastore:"key"` - Value string `json:"value" datastore:"value,noindex"` -} - -// Not environment, but execution environment -//type Environment struct { -// Name string `datastore:"name"` -// Type string `datastore:"type"` -// Registered bool `datastore:"registered"` -// Default bool `datastore:"default" json:"default"` -// Archived bool `datastore:"archived" json:"archived"` -// Id string `datastore:"id" json:"id"` -// OrgId string `datastore:"org_id" json:"org_id"` -//} - -// timeout maybe? idk -type session struct { - Username string `datastore:"Username,noindex"` - Id string `datastore:"Id,noindex"` - Session string `datastore:"session,noindex"` -} - -type loginStruct struct { - Username string `json:"username"` - Password string `json:"password"` -} - type Contact struct { Firstname string `json:"firstname"` Lastname string `json:"lastname"` @@ -3825,7 +3735,7 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error { //log.Printf("Remote Data: %s", respBody) err = remoteOrgJobController(org, respBody) if err != nil { - log.Printf("[ERROR] Failed job controller run for %s: %s", respBody, err) + //log.Printf("[ERROR] Failed cloud sync job controller run for '%s': %s", respBody, err) return err } return nil @@ -3869,7 +3779,7 @@ func runInitEs(ctx context.Context) { activeOrgs, err := shuffle.GetAllOrgs(ctx) setUsers := false - //log.Printf("ORGS: %d", len(activeOrgs)) + _ = setUsers if err != nil { if fmt.Sprintf("%s", err) == "EOF" { time.Sleep(7 * time.Second) @@ -3924,7 +3834,7 @@ func runInitEs(ctx context.Context) { if len(activeOrgs) == 1 { if len(activeOrgs[0].Users) == 0 { - log.Printf("ORG doesn't have any users??") + log.Printf("[ERROR] Main Org doesn't have any user. Creating.") users, err := shuffle.GetAllUsers(ctx) if err != nil && len(users) == 0 { @@ -3957,10 +3867,9 @@ func runInitEs(ctx context.Context) { if strings.Contains(os.Getenv("SHUFFLE_OPENSEARCH_URL"), "https") { log.Printf("[INFO] Waiting during init to make sure the opensearch instance is up and running with security features properly") - time.Sleep(30 * time.Second) + time.Sleep(15 * time.Second) } - _ = setUsers schedules, err := shuffle.GetAllSchedules(ctx, "ALL") if err != nil { log.Printf("[WARNING] Failed getting schedules during service init: %s", err) @@ -4104,11 +4013,11 @@ func runInitEs(ctx context.Context) { continue } - log.Printf("[DEBUG] Should start schedule for org %s (%s)", org.Name, org.Id) + log.Printf("[DEBUG] Should start cloud schedule for org %s (%s)", org.Name, org.Id) job := func() { err := remoteOrgJobHandler(org, interval) if err != nil { - log.Printf("[ERROR] Failed request with remote org setup (2): %s", err) + log.Printf("[ERROR] Failed request with remote org setup for org %s (2): %s", org.Id, err) } } diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 74676a45..4c29f71e 100755 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1692,6 +1692,8 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request return shuffle.WorkflowExecution{}, "Cloud not implemented yet", errors.New("Cloud not implemented yet") } + shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_cloud") + // What it needs to know: // 1. Parameters if len(workflowExecution.Workflow.Actions) == 1 { @@ -1705,13 +1707,11 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request // If worker, should this backend be a proxy? I think so. return shuffle.WorkflowExecution{}, "Cloud not implemented yet (2)", errors.New("Cloud not implemented yet") } + } else { + shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_onprem") } - //err = increaseStatisticsField(ctx, "workflow_executions", workflow.ID, 1, workflowExecution.ExecutionOrg) - //if err != nil { - // log.Printf("Failed to increase stats execution stats: %s", err) - //} - + shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions") return workflowExecution, "", nil } diff --git a/docker-compose.yml b/docker-compose.yml index 2eb61619..1b93335c 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - image: ghcr.io/shuffle/shuffle-frontend:latest + image: ghcr.io/shuffle/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -61,8 +61,9 @@ services: hostname: shuffle-opensearch container_name: shuffle-opensearch environment: - - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms2048m -Xmx2048m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM + - bootstrap.memory_lock=true + - DISABLE_PERFORMANCE_ANALYZER_AGENT_CLI=true - cluster.initial_master_nodes=shuffle-opensearch - cluster.routing.allocation.disk.threshold_enabled=false - cluster.name=shuffle-cluster diff --git a/frontend/src/components/AppFramework.jsx b/frontend/src/components/AppFramework.jsx index 9750ff7f..ca11154f 100644 --- a/frontend/src/components/AppFramework.jsx +++ b/frontend/src/components/AppFramework.jsx @@ -38,7 +38,7 @@ import { toast } from 'react-toastify'; cytoscape.use(edgehandles); const svgSize = "40px" -const parsedDatatypeImages = { +export const parsedDatatypeImages = { "SIEM": encodeURI(`data:image/svg+xml;utf-8,`), "CASES": encodeURI(`data:image/svg+xml;utf-8,`), @@ -713,7 +713,7 @@ const AppFramework = (props) => { } useEffect(() => { - console.log("DISCWRAP CHANG: ", discoveryWrapper) + //console.log("DISCWRAP CHANG: ", discoveryWrapper) if (discoveryWrapper === undefined || discoveryWrapper.id === "SHUFFLE" || discoveryWrapper.id === undefined || cy === undefined) { setDiscoveryData({}) @@ -874,7 +874,7 @@ const AppFramework = (props) => { }, []) useEffect(() => { - console.log("New selected app: ", newSelectedApp, discoveryData) + //console.log("New selected app: ", newSelectedApp, discoveryData) if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) { return } @@ -2065,8 +2065,8 @@ const AppFramework = (props) => { const foundelement = cy.getElementById(discoveryData.id) if (foundelement !== undefined && foundelement !== null) { - console.log("element: ", foundelement) - console.log("DISC: ", discoveryData) + //console.log("element: ", foundelement) + //console.log("DISC: ", discoveryData) foundelement.data("large_image", parsedDatatypeImages[discoveryData.id.toUpperCase()]) foundelement.data("text_margin_y", "14px") foundelement.data("margin_x", "32px") diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index 769ade88..9db5c484 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -12,6 +12,7 @@ import { Drawer, CircularProgress, IconButton, + Tooltip, } from "@mui/material"; import { @@ -34,12 +35,38 @@ const WorkflowTemplatePopup = (props) => { const [workflow, setWorkflow] = useState({}); const [appAuthentication, setAppAuthentication] = React.useState(undefined); + const imagestyleWrapper = { + height: 40, + width: 40, + borderRadius: 40, + border: "1px solid rgba(255,255,255,0.3)", + overflow: "hidden", + display: "flex", + } + + const imagestyleWrapperDefault = { + height: 40, + width: 40, + borderRadius: 40, + border: "1px solid red", + overflow: "hidden", + display: "flex", + } + const imagestyle = { height: 40, width: 40, borderRadius: 40, + border: "1px solid rgba(255,255,255,0.3)", + overflow: "hidden", } + const imagestyleDefault = { + display: "block", + marginLeft: 9, + marginTop: 10, + } + if (title === undefined || title === null || title === "") { console.log("No title for workflow template popup!"); return null @@ -297,7 +324,10 @@ const WorkflowTemplatePopup = (props) => { parsedTitle = title.substring(0, maxlength) + "..." } - parsedTitle.replaceAll("_", " ") + parsedTitle = parsedTitle.replaceAll("_", " ") + + const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : "" + return (
    @@ -334,7 +364,7 @@ const WorkflowTemplatePopup = (props) => { if (errorMessage !== "") { toast("Already failed to generate workflow for these apps. Please try again later or contact support@shuffler.io.") } else if (isActive) { - toast("Workflow already generated. Please try another template, or continue to the next page.") + toast("Workflow already generated. Please try another workflow template!") // FIXME: Remove these? loadAppAuth() @@ -350,22 +380,32 @@ const WorkflowTemplatePopup = (props) => { >
    - - {img2 !== "" ? - - - - + {img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ? + + + + + + : + + } + {img2 !== undefined && img2 !== "" && dstapp !== undefined && dstapp !== "" ? + + + + + + : }
    - + {parsedTitle} - {description} + {parsedDescription}
    diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 5871c03a..28b02f52 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -228,15 +228,16 @@ const parseCurl = (s) => { // Basically CRUD for each category + special export const appCategories = [ { - "name": "Communication", + "name": "Communication", "color": "#FFC107", "icon": "communication", "action_labels": ["List Messages", "Send Message", "Get Message", "Search messages", "List Attachments", "Get Attachment", "Get Contact"], - }, { + }, + { "name": "SIEM", "color": "#FFC107", "icon": "siem", - "action_labels": ["Search", "List Alerts", "Close Alert", "Get Alert", "Create detection", "Add to lookup list",], + "action_labels": ["Search", "List Alerts", "Close Alert", "Get Alert", "Create detection", "Add to lookup list", "Isolate endpoint",], }, { "name": "Eradication", "color": "#FFC107", diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 4f9ac5b3..d5e88e8c 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -287,6 +287,7 @@ const Apps = (props) => { const [selectedAction, setSelectedAction] = React.useState({}); const [searchBackend, setSearchBackend] = React.useState(false); const [searchableApps, setSearchableApps] = React.useState([]); + const [publishModalOpen, setPublishModalOpen] = React.useState(false); const [openApi, setOpenApi] = React.useState(""); const [openApiData, setOpenApiData] = React.useState(""); @@ -441,6 +442,7 @@ const Apps = (props) => { if (privateapps.length > 0) { if (selectedApp.id === undefined || selectedApp.id === null) { setSelectedApp(privateapps[0]); + setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you") } if ( @@ -646,6 +648,7 @@ const Apps = (props) => { if (selectedApp.id !== data.id) { data.name = newAppname; setSelectedApp(data); + setSharingConfiguration(data.sharing === true ? "public" : "you") if ( data.actions !== undefined && @@ -658,7 +661,7 @@ const Apps = (props) => { } if (data.sharing) { - setSharingConfiguration(isCloud ? "public" : "everyone"); + setSharingConfiguration("public"); } } }} @@ -999,11 +1002,11 @@ const Apps = (props) => { ); }; - const userRoles = ["you", isCloud ? "public" : "everyone"]; + const userRoles = ["you", "public"]; - // Admin in org or creator of app - // FIXME: Missing check for if same creator account - const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated + // Admin in org or creator of app + // FIXME: Missing check for if same creator account + const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated //fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"), var baseInfo = @@ -1054,6 +1057,7 @@ const Apps = (props) => { console.log("New version: ", newversion); selectedApp.app_version = selectedApp.app_version; setSelectedApp(selectedApp); + setSharingConfiguration(selectedApp.sharing === true ? "public" : "you") if (newversion !== undefined && newversion !== null) { getApp(newversion.id, true); @@ -1156,17 +1160,22 @@ const Apps = (props) => { - Beginner - Intermediate - Automation Ninja - - - - */} - - {/*Find your integrations!*/} -
    -
    -
    - - -
    -
    - - -
    - {/* - What do you want to automate first ? - - { onNodeSelect("Email") }} />} - label="Email" - labelPlacement="Email" - /> - { onNodeSelect("SIEM") }} />} - label="SIEM" - labelPlacement="SIEM" - /> - { onNodeSelect("EDR") }} />} - label="EDR" - labelPlacement="EDR" - /> - - */} -
    -
    -
    - -
    - - - ) - case 2: - return ( - -
    - -
    -
    -
    - -
    -
    -
    -
    -
    - ) - default: - return "unknown step" - } +
    +
    + + ) + default: + return "unknown step" } + } const extraHeight = isCloud ? -7 : 0; @@ -1081,7 +802,7 @@ const WelcomeForm = (props) => {
    }*/}
    - )} + )}
    ); diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index 9db5c484..f74c79af 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react"; import { toast } from "react-toastify" import theme from '../theme.jsx'; +import { useNavigate, Link, useParams } from "react-router-dom"; import { Button, Typography, @@ -33,8 +34,11 @@ const WorkflowTemplatePopup = (props) => { const [errorMessage, setErrorMessage] = useState(""); const [workflowLoading, setWorkflowLoading] = useState(false); const [workflow, setWorkflow] = useState({}); - const [appAuthentication, setAppAuthentication] = React.useState(undefined); + + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + let navigate = useNavigate(); + const imagestyleWrapper = { height: 40, width: 40, @@ -48,7 +52,7 @@ const WorkflowTemplatePopup = (props) => { height: 40, width: 40, borderRadius: 40, - border: "1px solid red", + border: "1px solid rgba(255,255,255,0.3)", overflow: "hidden", display: "flex", } @@ -63,8 +67,10 @@ const WorkflowTemplatePopup = (props) => { const imagestyleDefault = { display: "block", - marginLeft: 9, - marginTop: 10, + marginLeft: 11, + marginTop: 11, + height: 35, + width: "auto", } if (title === undefined || title === null || title === "") { @@ -102,6 +108,22 @@ const WorkflowTemplatePopup = (props) => { } const loadAppAuth = () => { + if (userdata === undefined || userdata === null) { + setErrorMessage("You need to be logged in to try usecases. Redirecting in 5 seconds...") + // Send the user to the login screen after 3 seconds + setTimeout(() => { + // Make it cancel if the state modalOpen changes + if (modalOpen === false) { + return + } + + navigate("/login?view=" + window.location.pathname + window.location.search) + }, 4500) + + return + } + + fetch(`${globalUrl}/api/v1/apps/authentication`, { method: "GET", headers: { @@ -168,7 +190,8 @@ const WorkflowTemplatePopup = (props) => { }, } - fetch(globalUrl + "/api/v1/workflows/merge", { + //fetch(globalUrl + "/api/v1/workflows/merge", { + fetch("https://shuffler.io/api/v1/workflows/merge", { method: "POST", headers: { "Content-Type": "application/json", @@ -226,8 +249,6 @@ const WorkflowTemplatePopup = (props) => { return true } - console.log("APPCONFIG: ", appconfig) - return false } @@ -309,6 +330,9 @@ const WorkflowTemplatePopup = (props) => { @@ -330,7 +354,7 @@ const WorkflowTemplatePopup = (props) => { return ( -
    +
    { //setIsActive(!isActive) if (errorMessage !== "") { - toast("Already failed to generate workflow for these apps. Please try again later or contact support@shuffler.io.") + toast("Already failed to generate a workflow for this usecase. Please try again later or contact support@shuffler.io.") } else if (isActive) { toast("Workflow already generated. Please try another workflow template!") @@ -393,7 +417,9 @@ const WorkflowTemplatePopup = (props) => { - + + + : diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index e3c893b5..346609f4 100755 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -480,7 +480,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user }) .then((response) => response.json().then((responseJson) => { - if (responseJson["success"] === false) { + if (responseJson.success === false) { toast("Failed deleting auth"); } else { // Need to wait because query in ES is too fast @@ -1410,7 +1410,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user return response.json(); }) .then((responseJson) => { - if (responseJson.success) { + if (responseJson.success === true) { //console.log(responseJson.data) //console.log(responseJson) setAuthentication(responseJson.data); diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 28b02f52..16dfdf23 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -367,8 +367,8 @@ const AppCreator = (defaultprops) => { let navigate = useNavigate(); const increaseAmount = 50; - const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"]; - const actionBodyRequest = ["POST", "PUT", "PATCH"]; + const actionNonBodyRequest = ["GET", "HEAD", "CONNECT"]; + const actionBodyRequest = ["POST", "PUT", "PATCH", "DELETE"]; const authenticationOptions = [ "No authentication", "API key", diff --git a/frontend/src/views/Dashboard.jsx b/frontend/src/views/Dashboard.jsx index 167ab17b..94317c55 100755 --- a/frontend/src/views/Dashboard.jsx +++ b/frontend/src/views/Dashboard.jsx @@ -13,6 +13,7 @@ import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx"; //import { useAlert import { ToastContainer, toast } from "react-toastify" import { parsedDatatypeImages } from "../components/AppFramework.jsx" +import { findSpecificApp } from "../components/AppFramework.jsx" import { Autocomplete, @@ -92,6 +93,8 @@ const useStyles = makeStyles({ }, }); + + const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => { const [expandedIndex, setExpandedIndex] = useState(-1); const [expandedItem, setExpandedItem] = useState(-1); @@ -160,136 +163,6 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData return null } - const findSpecificApp = (framework, inputcategory) => { - // Get the frameworkinfo for the org and fill in - // - if (framework === undefined || framework === null) { - console.log("findSpecificApp: frameworkData is null") - return null - } - - if (inputcategory === undefined || inputcategory === null) { - console.log("findSpecificApp: category is null") - return null - } - - const category = inputcategory.toLowerCase() - - console.log("findSpecificApp: ", category, frameworkData) - if (category === "edr" || category === "eradication" || category === "edr & av") { - if (frameworkData["EDR & AV"] !== undefined && frameworkData["EDR & AV"].name !== undefined) { - return frameworkData["EDR & AV"] - } - - return { - name: "EDR :default", - large_image: parsedDatatypeImages["EDR & AV"], - count: 0, - description: "", - id: "", - } - } else if (category === "communication") { - if (frameworkData["Comms"] !== undefined && frameworkData["Comms"].name !== undefined) { - return frameworkData["Comms"] - } - - return { - name: "COMMS :default", - large_image: parsedDatatypeImages["COMMS"], - count: 0, - description: "", - id: "", - } - } else if (category === "email") { - if (frameworkData["Email"] !== undefined && frameworkData["Email"].name !== undefined) { - return frameworkData["Email"] - } - - return { - name: "COMMS :default", - large_image: parsedDatatypeImages["COMMS"], - count: 0, - description: "", - id: "", - } - } else if (category === "assets") { - if (frameworkData["Assets"] !== undefined && frameworkData["Assets"].name !== undefined) { - return frameworkData["Assets"] - } - - return { - name: "ASSETS :default", - large_image: parsedDatatypeImages["ASSETS"], - count: 0, - description: "", - id: "", - } - } else if (category === "cases") { - if (frameworkData["Cases"] !== undefined && frameworkData["Cases"].name !== undefined) { - return frameworkData["Cases"] - } - - return { - name: "CASES :default", - large_image: parsedDatatypeImages["CASES"], - count: 0, - description: "", - id: "", - } - } else if (category === "iam") { - if (frameworkData["IAM"] !== undefined && frameworkData["IAM"].name !== undefined) { - return frameworkData["IAM"] - } - - return { - name: "EDR :default", - large_image: parsedDatatypeImages["EDR & AV"], - count: 0, - description: "", - id: "", - } - } else if (category === "network") { - if (frameworkData["Network"] !== undefined && frameworkData["Network"].name !== undefined) { - return frameworkData["Network"] - } - - return { - name: "Network :default", - large_image: parsedDatatypeImages["NETWORK"], - count: 0, - description: "", - id: "", - } - } else if (category === "intel") { - if (frameworkData["Intel"] !== undefined && frameworkData["Intel"].name !== undefined) { - return frameworkData["Intel"] - } - - return { - name: "INTEL :default", - large_image: parsedDatatypeImages["INTEL"], - count: 0, - description: "", - id: "", - } - } else if (category === "siem") { - if (frameworkData["SIEM"] !== undefined && frameworkData["SIEM"].name !== undefined) { - return frameworkData["SIEM"] - } - - return { - name: "SIEM :default", - large_image: parsedDatatypeImages["SIEM"], - count: 0, - description: "", - id: "", - } - } else { - console.log("findSpecificApp: unknown category: ", category) - } - - return null - } const parseUsecase = (subcase) => { console.log("parseUsecase: ", subcase) @@ -340,12 +213,6 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData var parsedUsecase = responseJson if (responseJson.success === false) { - - //img1={inputUsecase.srcimg} - //srcapp={inputUsecase.srcapp} - //img2={inputUsecase.dstimg} - //dstapp={inputUsecase.dstapp} - //title={inputUsecase.name} parsedUsecase = subcase } else { parsedUsecase = responseJson @@ -354,13 +221,9 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData parsedUsecase.srcapp = subcase.srcapp parsedUsecase.dstimg = subcase.dstimg parsedUsecase.dstapp = subcase.dstapp - - //parsedUsecase = parseUsecase(responseJson) } // Look for the type of app and fill in img1, srcapp... - console.log("USECASE: ", parsedUsecase) - setInputUsecase(parsedUsecase) setExpandedIndex(index) setExpandedItem(subindex) @@ -861,18 +724,30 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
    : -
    - - {subcase.description} - +
    + + {subcase.description} + - {workflows !== undefined && workflows !== null && workflows.length > 0 ? - - Select relevant workflows - - : null} + {workflows !== undefined && workflows !== null && workflows.length > 0 ? + + Select relevant workflows + + : + + + Find workflows related to this usecase: + + + + + + + + + } - {workflows !== undefined && workflows !== null && workflows.length > 0 ? + {workflows !== undefined && workflows !== null && workflows.length > 0 ? : null} - {}}> - Try this Usecase + {}} + > + Try it out: { - const { globalUrl, surfaceColor, newColor, mini, inputColor, userdata, isLoggedIn, isLoaded, serverside } = props; + const { globalUrl, surfaceColor, newColor, mini, inputColor, userdata, isLoggedIn, isLoaded, serverside, checkLogin } = props; const [skipped, setSkipped] = React.useState(new Set()); const [inputUsecase, setInputUsecase] = useState({}); const [frameworkData, setFrameworkData] = useState(undefined); const [discoveryWrapper, setDiscoveryWrapper] = useState(undefined); const [activeStep, setActiveStep] = React.useState(1); const [apps, setApps] = React.useState([]); - const [defaultSearch, setDefaultSearch] = React.useState("") - const [selectionOpen, setSelectionOpen] = React.useState(false) - const [showWelcome, setShowWelcome] = React.useState(false) + const [defaultSearch, setDefaultSearch] = React.useState("") + const [selectionOpen, setSelectionOpen] = React.useState(false) + const [showWelcome, setShowWelcome] = React.useState(false) const [usecases, setUsecases] = React.useState([]); const [workflows, setWorkflows] = React.useState([]); - let navigate = useNavigate(); - //if (serverside === false && isLoaded === true && isLoggedIn === false) { - // console.log("Redirecting to login?") - // console.log(window.location.pathname) - // console.log(window.location) - // navigate(`/login?view=${window.location.pathname}${window.location.search}`) - //} + let navigate = useNavigate(); + useEffect(() => { + console.log("IN USEEFFECT FOR ACTIVESTEP: ", activeStep, " and with login: ", checkLogin) + if (checkLogin !== undefined) { + checkLogin() + } + }, [activeStep]) const isCloud = window.location.host === "localhost:3002" || @@ -396,6 +396,7 @@ const Welcome = (props) => { /> */} { globalUrl={globalUrl} priority={foundPriority} checkLogin={checkLogin} + appFramework={appFramework} /> : null} @@ -3531,6 +3532,7 @@ const Workflows = (props) => { globalUrl={globalUrl} priority={foundPriority} checkLogin={checkLogin} + appFramework={appFramework} /> : null} From 5a4e80992b4dc5b8428ba60d5d4cf2ca1f42db24 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:30:37 +0530 Subject: [PATCH 099/160] fix: better minikube docs --- functions/kubernetes/kubernetes.md | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index cf8a6f03..f3e75d1e 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -1,8 +1,8 @@ ## How to deploy Shuffle on Kubernetes? ### Prerequisites: -- Clone the https://github.com/dhaval055/shuffle repository using Git and change the branch to shuffle-k8s then navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. -- Running Kubernetes cluster. +- Clone the https://github.com/shuffle/shuffle repository using Git then, navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. +- Running Kubernetes cluster. You can do that with either minikube or run the cluster locally. If you're running on minikube, make sure to have exposed the ports ```30007-30008```, which you can do by running minikube like this: ```minikube start --extra-config=apiserver.service-node-port-range=30007-30008``` - Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. To setup a docker registry, if you have docker installed on one of your node run following commands. ``` openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/reg.key -x509 -days 365 -out certs/reg.crt -subj "/CN=" @@ -24,21 +24,9 @@ ``` Step 1: Create a namespace called shuffle in a cluster by running ```kubectl create ns shuffle```. -Step 2: Open the shuffle-cm.yaml file and review the configuration values. Change the value of REGISTRY_URL with ':5000' where the registry is at. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap with ‘kubectl apply -f shuffle-cm.yaml -n shuffle’. +Step 2: Open the ```all-in-one.yaml``` file and review the configuration values. Change the value of REGISTRY_URL with ':5000' where the registry is at. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap with ```kubectl apply -f shuffle-cm.yaml -n shuffle```. -Step 3: Apply shuffle-role.yaml by using ```kubectl apply -f shuffle-role.yaml -n shuffle```. - -Step 4: Subsequently, establish the role bindings to associate the roles with the appropriate service accounts by applying the shuffle-rolebinding.yaml configuration using ```kubectl apply -f shuffle-rolebinding.yaml -n shuffle```. - -Step 5: Create storage-class for shuffle by running ```kubectl apply -f shuffle-sc.yaml -n shuffle```. - -Step 6: Apply the shuffle-opensearch.yaml configuration to create a Persistent Volume Claim (PVC) for storage, a Deployment to manage the OpenSearch pod, and a Service to expose OpenSearch within the cluster by using ```kubectl apply -f shuffle-opensearch.yaml -n shuffle```. - -Step 7: Now deploy the backend using ```kubectl apply -f shuffle-backend.yaml -n shuffle```. - -Step 8: Ensure that the Shuffle Backend is successfully deployed and running before proceeding with the frontend deployment, as the frontend may depend on services provided by the backend. ```kubectl apply -f shuffle-frontend.yaml -n shuffle```. - -Step 9: Run ```kubectl apply -f shuffle-orborus.yaml -n shuffle```. This final step ensures that Shuffle Orborus is deployed, enabling the workflow executions in your Kubernetes-based Shuffle setup. +Step 3: Now, Do ```kubectl apply -f all-in-one.yaml -n shuffle```. This will deploy all the necessary components of Shuffle! Now, open ```https://:30008``` or ```http://:30007```. You should be seeing a signup page. NODE_IP should be where the frontend is deployed. From 2eec88cb966fa934afa60715d77b1fc542d912a5 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:31:34 +0530 Subject: [PATCH 100/160] fix: removing unnecessary files --- functions/kubernetes/shuffle-backend.yaml | 431 ------------------ functions/kubernetes/shuffle-cm.yaml | 64 --- functions/kubernetes/shuffle-frontend.yaml | 68 --- functions/kubernetes/shuffle-opensearch.yaml | 136 ------ functions/kubernetes/shuffle-orborus.yaml | 67 --- functions/kubernetes/shuffle-role.yaml | 12 - functions/kubernetes/shuffle-rolebinding.yaml | 14 - functions/kubernetes/shuffle-sc.yaml | 7 - 8 files changed, 799 deletions(-) delete mode 100644 functions/kubernetes/shuffle-backend.yaml delete mode 100644 functions/kubernetes/shuffle-cm.yaml delete mode 100644 functions/kubernetes/shuffle-frontend.yaml delete mode 100644 functions/kubernetes/shuffle-opensearch.yaml delete mode 100644 functions/kubernetes/shuffle-orborus.yaml delete mode 100644 functions/kubernetes/shuffle-role.yaml delete mode 100644 functions/kubernetes/shuffle-rolebinding.yaml delete mode 100644 functions/kubernetes/shuffle-sc.yaml diff --git a/functions/kubernetes/shuffle-backend.yaml b/functions/kubernetes/shuffle-backend.yaml deleted file mode 100644 index 62c3cdbf..00000000 --- a/functions/kubernetes/shuffle-backend.yaml +++ /dev/null @@ -1,431 +0,0 @@ ---- - -apiVersion: v1 -kind: PersistentVolume -metadata: - name: shuffle-apps-pv -spec: - capacity: - storage: 10Gi - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: shuffle-data - hostPath: - path: /mnt/shuffle-data/backend - -## --- - -# apiVersion: v1 -# kind: PersistentVolume -# metadata: -# name: shuffle-files-pv -# spec: -# capacity: -# storage: 5Gi -# accessModes: -# - ReadWriteOnce -# persistentVolumeReclaimPolicy: Retain -# storageClassName: fast -# hostPath: -# path: /mnt/shuffle-data/backend - -# --- - -# apiVersion: v1 -# kind: PersistentVolumeClaim -# metadata: -# creationTimestamp: null -# labels: -# io.kompose.service: backend-files-claim -# name: backend-files-claim -# spec: -# accessModes: -# - ReadWriteOnce -# storageClassName: fast -# resources: -# requests: -# storage: 1Gi -# status: {} - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: backend-apps-claim - name: backend-apps-claim -spec: - accessModes: - - ReadWriteOnce - storageClassName: shuffle-data - resources: - requests: - storage: 1Gi -# status: {} - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: backend - name: backend -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: backend - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: backend - app: shuffle-backend - name: shuffle-backend - spec: - volumes: - # - name: shuffle-files - # persistentVolumeClaim: - # claimName: backend-files-claim - # - name: shuffle-apps - # hostPath: - # path: /mnt/shuffle-data/backend - # type: DirectoryOrCreate - - name: shuffle-apps - persistentVolumeClaim: - claimName: backend-apps-claim - # - name: docker-socket - # hostPath: - # path: /var/run/docker.sock -# nodeSelector: -# node: master - containers: - - env: - - name: BACKEND_HOSTNAME - valueFrom: - configMapKeyRef: - key: BACKEND_HOSTNAME - name: env - - name: BACKEND_PORT - valueFrom: - configMapKeyRef: - key: BACKEND_PORT - name: env - - name: BASE_URL - valueFrom: - configMapKeyRef: - key: BASE_URL - name: env - - name: DATASTORE_EMULATOR_HOST - valueFrom: - configMapKeyRef: - key: DATASTORE_EMULATOR_HOST - name: env - - name: DB_LOCATION - valueFrom: - configMapKeyRef: - key: DB_LOCATION - name: env - - name: DOCKER_API_VERSION - valueFrom: - configMapKeyRef: - key: DOCKER_API_VERSION - name: env - - name: ENVIRONMENT_NAME - valueFrom: - configMapKeyRef: - key: ENVIRONMENT_NAME - name: env - - name: FRONTEND_PORT - valueFrom: - configMapKeyRef: - key: FRONTEND_PORT - name: env - - name: FRONTEND_PORT_HTTPS - valueFrom: - configMapKeyRef: - key: FRONTEND_PORT_HTTPS - name: env - - name: HTTPS_PROXY - valueFrom: - configMapKeyRef: - key: HTTPS_PROXY - name: env - - name: HTTP_PROXY - valueFrom: - configMapKeyRef: - key: HTTP_PROXY - name: env - - name: ORBORUS_CONTAINER_NAME - valueFrom: - configMapKeyRef: - key: ORBORUS_CONTAINER_NAME - name: env - - name: ORG_ID - valueFrom: - configMapKeyRef: - key: ORG_ID - name: env - - name: OUTER_HOSTNAME - valueFrom: - configMapKeyRef: - key: OUTER_HOSTNAME - name: env - - name: SHUFFLE_APP_DOWNLOAD_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_DOWNLOAD_LOCATION - name: env - - name: SHUFFLE_APP_FORCE_UPDATE - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_FORCE_UPDATE - name: env - - name: SHUFFLE_APP_HOTLOAD_FOLDER - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_HOTLOAD_FOLDER - name: env - - name: SHUFFLE_APP_HOTLOAD_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_APP_HOTLOAD_LOCATION - name: env - - name: SHUFFLE_BASE_IMAGE_NAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_BASE_IMAGE_NAME - name: env - - name: SHUFFLE_BASE_IMAGE_REGISTRY - valueFrom: - configMapKeyRef: - key: SHUFFLE_BASE_IMAGE_REGISTRY - name: env - - name: SHUFFLE_BASE_IMAGE_TAG_SUFFIX - valueFrom: - configMapKeyRef: - key: SHUFFLE_BASE_IMAGE_TAG_SUFFIX - name: env - - name: SHUFFLE_CHAT_DISABLED - valueFrom: - configMapKeyRef: - key: SHUFFLE_CHAT_DISABLED - name: env - - name: SHUFFLE_CONTAINER_AUTO_CLEANUP - valueFrom: - configMapKeyRef: - key: SHUFFLE_CONTAINER_AUTO_CLEANUP - name: env - - name: SHUFFLE_DEFAULT_APIKEY - valueFrom: - configMapKeyRef: - key: SHUFFLE_DEFAULT_APIKEY - name: env - - name: SHUFFLE_DEFAULT_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_DEFAULT_PASSWORD - name: env - - name: SHUFFLE_DEFAULT_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_DEFAULT_USERNAME - name: env - - name: SHUFFLE_DOWNLOAD_AUTH_BRANCH - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_AUTH_BRANCH - name: env - - name: SHUFFLE_DOWNLOAD_AUTH_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_AUTH_PASSWORD - name: env - - name: SHUFFLE_DOWNLOAD_AUTH_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_AUTH_USERNAME - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD - name: env - - name: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME - name: env - - name: SHUFFLE_ELASTIC - valueFrom: - configMapKeyRef: - key: SHUFFLE_ELASTIC - name: env - - name: SHUFFLE_ENCRYPTION_MODIFIER - valueFrom: - configMapKeyRef: - key: SHUFFLE_ENCRYPTION_MODIFIER - name: env - - name: SHUFFLE_FILE_LOCATION - valueFrom: - configMapKeyRef: - key: SHUFFLE_FILE_LOCATION - name: env - - name: SHUFFLE_LOGS_DISABLED - valueFrom: - configMapKeyRef: - key: SHUFFLE_LOGS_DISABLED - name: env - - name: SHUFFLE_OPENSEARCH_APIKEY - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_APIKEY - name: env - - name: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE - name: env - - name: SHUFFLE_OPENSEARCH_CLOUDID - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_CLOUDID - name: env - - name: SHUFFLE_OPENSEARCH_INDEX_PREFIX - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_INDEX_PREFIX - name: env - - name: SHUFFLE_OPENSEARCH_PASSWORD - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_PASSWORD - name: env - - name: SHUFFLE_OPENSEARCH_PROXY - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_PROXY - name: env - - name: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY - name: env - - name: SHUFFLE_OPENSEARCH_URL - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_URL - name: env - - name: SHUFFLE_OPENSEARCH_USERNAME - valueFrom: - configMapKeyRef: - key: SHUFFLE_OPENSEARCH_USERNAME - name: env - - name: SHUFFLE_ORBORUS_STARTUP_DELAY - valueFrom: - configMapKeyRef: - key: SHUFFLE_ORBORUS_STARTUP_DELAY - name: env - - name: SHUFFLE_PASS_APP_PROXY - valueFrom: - configMapKeyRef: - key: SHUFFLE_PASS_APP_PROXY - name: env - - name: SHUFFLE_PASS_WORKER_PROXY - valueFrom: - configMapKeyRef: - key: SHUFFLE_PASS_WORKER_PROXY - name: env - - name: SHUFFLE_RERUN_SCHEDULE - valueFrom: - configMapKeyRef: - key: SHUFFLE_RERUN_SCHEDULE - name: env - - name: SSO_REDIRECT_URL - valueFrom: - configMapKeyRef: - key: SSO_REDIRECT_URL - name: env - - name: TZ - valueFrom: - configMapKeyRef: - key: TZ - name: env - - name: IS_KUBERNETES - valueFrom: - configMapKeyRef: - key: IS_KUBERNETES - name: env - - name: REGISTRY_URL - valueFrom: - configMapKeyRef: - key: REGISTRY_URL - name: env - - name: REGISTRY_AUTH - valueFrom: - configMapKeyRef: - key: REGISTRY_AUTH - name: env - image: dhavald055/shuffle-k8s:shuffle-backend-kubernetes - imagePullPolicy: Always - name: shuffle-backend - ports: - - containerPort: 5001 - resources: {} - volumeMounts: - # - name: docker-socket - # mountPath: /var/run/docker.sock - - name: shuffle-apps - mountPath: /app/generated - # - name: shuffle-files - # mountPath: /shuffle-files - restartPolicy: Always -status: {} - ---- - -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: backend - name: shuffle-backend -spec: - type: NodePort - ports: - - name: "50001" - port: 5001 - targetPort: 5001 - nodePort: 30009 - selector: - io.kompose.service: backend -status: - loadBalancer: {} - diff --git a/functions/kubernetes/shuffle-cm.yaml b/functions/kubernetes/shuffle-cm.yaml deleted file mode 100644 index cf3b6641..00000000 --- a/functions/kubernetes/shuffle-cm.yaml +++ /dev/null @@ -1,64 +0,0 @@ -apiVersion: v1 -data: - BACKEND_HOSTNAME: shuffle-backend - BACKEND_PORT: "5001" - BASE_URL: http://shuffle-backend:5001 - DATASTORE_EMULATOR_HOST: shuffle-database:8000 - DB_LOCATION: /mnt/shuffle-data/open-search - DOCKER_API_VERSION: "1.40" - ENVIRONMENT_NAME: Shuffle - FRONTEND_PORT: "3001" - FRONTEND_PORT_HTTPS: "3443" - HTTP_PROXY: "" - HTTPS_PROXY: "" - ORBORUS_CONTAINER_NAME: "\t\t\t\t" - ORG_ID: Shuffle - OUTER_HOSTNAME: shuffle-backend - SHUFFLE_APP_DOWNLOAD_LOCATION: https://github.com/shuffle/python-apps - SHUFFLE_APP_FORCE_UPDATE: "false" - SHUFFLE_APP_HOTLOAD_FOLDER: /shuffle-apps - SHUFFLE_APP_HOTLOAD_LOCATION: ./shuffle-apps - SHUFFLE_BASE_IMAGE_NAME: shuffle - SHUFFLE_BASE_IMAGE_REGISTRY: ghcr.io - SHUFFLE_BASE_IMAGE_TAG_SUFFIX: -1.0.0 - SHUFFLE_CHAT_DISABLED: "false" - SHUFFLE_CONTAINER_AUTO_CLEANUP: "false" - SHUFFLE_DEFAULT_APIKEY: "" - SHUFFLE_DEFAULT_PASSWORD: "" - SHUFFLE_DEFAULT_USERNAME: "" - SHUFFLE_DOWNLOAD_AUTH_BRANCH: "" - SHUFFLE_DOWNLOAD_AUTH_PASSWORD: "" - SHUFFLE_DOWNLOAD_AUTH_USERNAME: "" - SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH: "" - SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION: "" - SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD: "" - SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME: "" - SHUFFLE_ELASTIC: "true" - SHUFFLE_ENCRYPTION_MODIFIER: "" - SHUFFLE_FILE_LOCATION: /shuffle-files - SHUFFLE_LOGS_DISABLED: "false" - SHUFFLE_OPENSEARCH_APIKEY: "" - SHUFFLE_OPENSEARCH_CERTIFICATE_FILE: "" - SHUFFLE_OPENSEARCH_CLOUDID: "" - SHUFFLE_OPENSEARCH_INDEX_PREFIX: "" - SHUFFLE_OPENSEARCH_PASSWORD: admin - SHUFFLE_OPENSEARCH_PROXY: "" - SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY: "true" - SHUFFLE_OPENSEARCH_URL: https://opensearch:9200 - SHUFFLE_OPENSEARCH_USERNAME: admin - SHUFFLE_ORBORUS_STARTUP_DELAY: "\t\t" - SHUFFLE_PASS_APP_PROXY: "FALSE" - SHUFFLE_PASS_WORKER_PROXY: "TRUE" - SHUFFLE_RERUN_SCHEDULE: "300" - SSO_REDIRECT_URL: "" - TZ: "Europe/Amsterdam \t\t\t\t\t" - IS_KUBERNETES: "true" - REGISTRY_URL: "172.17.14.71:5000" - REGISTRY_AUTH: "true" #set it to true if you are using registry with authentication - SHUFFLE_KUBERNETES_WORKER: "dhavald055/shuffle-k8s:shuffle-worker-kubernetes" -kind: ConfigMap -metadata: - creationTimestamp: null - labels: - io.kompose.service: backend-env - name: env diff --git a/functions/kubernetes/shuffle-frontend.yaml b/functions/kubernetes/shuffle-frontend.yaml deleted file mode 100644 index cb11f728..00000000 --- a/functions/kubernetes/shuffle-frontend.yaml +++ /dev/null @@ -1,68 +0,0 @@ ---- - -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: frontend - name: frontend -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: frontend - strategy: {} - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: frontend - spec: - containers: - - env: - - name: BACKEND_HOSTNAME - image: ghcr.io/shuffle/shuffle-frontend:latest - name: shuffle-frontend - ports: - - containerPort: 80 - - containerPort: 443 - resources: {} - hostname: shuffle-frontend - restartPolicy: Always -status: {} - ---- - -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: frontend - name: frontend -spec: - type: NodePort - ports: - - name: "80" - port: 80 - targetPort: 80 - nodePort: 30007 - - name: "443" - port: 443 - targetPort: 443 - nodePort: 30008 - selector: - io.kompose.service: frontend -# status: -# loadBalancer: {} diff --git a/functions/kubernetes/shuffle-opensearch.yaml b/functions/kubernetes/shuffle-opensearch.yaml deleted file mode 100644 index 09a7c03c..00000000 --- a/functions/kubernetes/shuffle-opensearch.yaml +++ /dev/null @@ -1,136 +0,0 @@ ---- - -apiVersion: v1 -kind: PersistentVolume -metadata: - name: shuffle-os-pv -spec: - capacity: - storage: 10Gi # Adjust the storage size as per your requirements - accessModes: - - ReadWriteOnce # This allows read-write access to a single node - persistentVolumeReclaimPolicy: Retain # Adjust the reclaim policy as per your needs - storageClassName: shuffle-data # Set the desired storage class - hostPath: - path: /mnt/shuffle-data/open-search - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: opensearch-claim0 - name: opensearch-claim0 -spec: - accessModes: - - ReadWriteOnce - storageClassName: shuffle-data - resources: - requests: - storage: 500Mi -status: {} - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: opensearch - name: opensearch -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: opensearch - strategy: {} - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: opensearch - spec: - # securityContext: - # runAsUser: 1000 # UID - # fsGroup: 1000 # GID - # nodeSelector: - # node: worker1 - initContainers: - - name: volume-permissions - image: busybox - command: ["sh", "-c", "chown -R 1000:1000 /usr/share/opensearch/data"] - volumeMounts: - - name: opensearch-claim0 - mountPath: /usr/share/opensearch/data - containers: - - env: - - name: OPENSEARCH_JAVA_OPTS - value: -Xms1024m -Xmx1024m - #- name: bootstrap.memory_lock - #value: "true" - - name: cluster.initial_master_nodes - value: shuffle-opensearch - - name: cluster.name - value: shuffle-cluster - - name: cluster.routing.allocation.disk.threshold_enabled - value: "false" - - name: discovery.seed_hosts - value: shuffle-opensearch - - name: node.name - value: shuffle-opensearch - - name: node.store.allow_mmap - value: "false" - - name: DB_LOCATION - valueFrom: - configMapKeyRef: - name: env - key: DB_LOCATION - image: opensearchproject/opensearch:2.5.0 - name: shuffle-opensearch - ports: - - containerPort: 9200 - resources: {} - volumeMounts: - - mountPath: /usr/share/opensearch/data - name: opensearch-claim0 - hostname: shuffle-opensearch - restartPolicy: Always - volumes: - - name: opensearch-claim0 - persistentVolumeClaim: - claimName: opensearch-claim0 -status: {} - ---- - -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: opensearch - name: opensearch -spec: - type: NodePort - ports: - - name: "9200" - port: 9200 - targetPort: 9200 - nodePort: 31001 - selector: - io.kompose.service: opensearch -status: - loadBalancer: {} - diff --git a/functions/kubernetes/shuffle-orborus.yaml b/functions/kubernetes/shuffle-orborus.yaml deleted file mode 100644 index fd5f0a6f..00000000 --- a/functions/kubernetes/shuffle-orborus.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.service: orborus - name: orborus -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: orborus - strategy: {} - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose.yml - kompose.version: 1.26.0 (40646f47) - creationTimestamp: null - labels: - io.kompose.network/shuffle: "true" - io.kompose.service: orborus - spec: - containers: - - env: - - name: BASE_URL - value: "http://shuffle-backend:5001" - - name: DOCKER_API_VERSION - value: "1.40" - - name: ENVIRONMENT_NAME - value: Shuffle - - name: ORG_ID - value: Shuffle - - name: SHUFFLE_APP_SDK_VERSION - value: nightly - - name: SHUFFLE_SCALE_REPLICAS - value: "5" - #- name: SHUFFLE_SWARM_CONFIG - #value: run - - name: SHUFFLE_WORKER_VERSION - value: nightly - - name: IS_KUBERNETES - valueFrom: - configMapKeyRef: - key: IS_KUBERNETES - name: env - - name: REGISTRY_URL - valueFrom: - configMapKeyRef: - key: REGISTRY_URL - name: env - - name: SHUFFLE_KUBERNETES_WORKER - valueFrom: - configMapKeyRef: - key: SHUFFLE_KUBERNETES_WORKER - name: env - - image: dhavald055/shuffle-k8s:shuffle-orborus-kubernetes - #imagePullPolicy: Never - name: shuffle-orborus - resources: {} - hostname: shuffle-orborus - restartPolicy: Always -status: {} diff --git a/functions/kubernetes/shuffle-role.yaml b/functions/kubernetes/shuffle-role.yaml deleted file mode 100644 index bf2afbe9..00000000 --- a/functions/kubernetes/shuffle-role.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - namespace: shuffle - name: pod-manager -rules: -- apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "create", "update", "delete"] -- apiGroups: ["batch"] - resources: ["jobs"] - verbs: ["create", "get", "list", "watch", "delete"] diff --git a/functions/kubernetes/shuffle-rolebinding.yaml b/functions/kubernetes/shuffle-rolebinding.yaml deleted file mode 100644 index fd2225f6..00000000 --- a/functions/kubernetes/shuffle-rolebinding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: pod-manager-binding - namespace: shuffle -subjects: -- kind: ServiceAccount - name: default - namespace: shuffle -roleRef: - kind: Role - name: pod-manager - apiGroup: rbac.authorization.k8s.io - diff --git a/functions/kubernetes/shuffle-sc.yaml b/functions/kubernetes/shuffle-sc.yaml deleted file mode 100644 index a8cb64c5..00000000 --- a/functions/kubernetes/shuffle-sc.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: shuffle-data -provisioner: kubernetes.io/no-provisioner -volumeBindingMode: WaitForFirstConsumer - From adba5db978365e653680508deaf7994dae9c9430 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:33:23 +0530 Subject: [PATCH 101/160] fix: adding helpful links --- functions/kubernetes/kubernetes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index f3e75d1e..384a9082 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -2,7 +2,7 @@ ### Prerequisites: - Clone the https://github.com/shuffle/shuffle repository using Git then, navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. -- Running Kubernetes cluster. You can do that with either minikube or run the cluster locally. If you're running on minikube, make sure to have exposed the ports ```30007-30008```, which you can do by running minikube like this: ```minikube start --extra-config=apiserver.service-node-port-range=30007-30008``` +- [Running a Kubernetes cluster](https://kubernetes.io/docs/setup/). You can do that with either minikube or run the cluster locally. If you're running on minikube, make sure to have exposed the ports ```30007-30008```, which you can do by running minikube like this: ```minikube start --extra-config=apiserver.service-node-port-range=30007-30008``` - Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. To setup a docker registry, if you have docker installed on one of your node run following commands. ``` openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/reg.key -x509 -days 365 -out certs/reg.crt -subj "/CN=" From 168817b3a727e8aeb6db95495f91b45b23f77674 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:42:58 +0530 Subject: [PATCH 102/160] fix: adding helpful cert generation script --- functions/kubernetes/generate_certs.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100755 functions/kubernetes/generate_certs.sh diff --git a/functions/kubernetes/generate_certs.sh b/functions/kubernetes/generate_certs.sh new file mode 100755 index 00000000..606ea2fa --- /dev/null +++ b/functions/kubernetes/generate_certs.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +read -p "Are you using Minikube? (yes/no): " is_minikube + +if [ "$is_minikube" == "yes" ]; then + node_ip=$(minikube ip) +else + read -p "Enter your node IP (default is localhost): " custom_ip + # Use localhost as the default value + node_ip=${custom_ip:-localhost} +fi + +echo "Using node IP: $node_ip to generate SSL certs!" + +# Generate CA key +openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/reg.key -x509 -days 365 -out certs/reg.crt -subj "/CN=$node_ip" + + From f4ebe2d0461e858acbefd10cde5533eb7314a09f Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 19 Oct 2023 12:37:04 +0200 Subject: [PATCH 103/160] More polishing in the onboarding and usecase generation process --- backend/go-app/main.go | 10 +- frontend/src/components/AppSelection.jsx | 569 ++++++++++++++++++ .../src/components/WorkflowTemplatePopup.jsx | 4 +- 3 files changed, 579 insertions(+), 4 deletions(-) create mode 100644 frontend/src/components/AppSelection.jsx diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 63448dc4..ba444b10 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4147,8 +4147,8 @@ func runInitEs(ctx context.Context) { if err != nil && len(workflowapps) == 0 { log.Printf("[WARNING] Failed getting apps (runInit): %s", err) - } else if err == nil { - log.Printf("[DEBUG] Downloading default apps") + } else if err == nil && len(workflowapps) < 10 { + log.Printf("[DEBUG] Downloading default apps as %d were found", len(workflowapps)) fs := memfs.New() storer := memory.NewStorage() @@ -4204,6 +4204,8 @@ func runInitEs(ctx context.Context) { if len(location) != 0 { handleAppHotload(ctx, location, false) } + } else { + log.Printf("[DEBUG] Skipping download of default apps as %d were found", len(workflowapps)) } log.Printf("[INFO] Downloading OpenAPI data for search - EXTRA APPS") @@ -4219,7 +4221,7 @@ func runInitEs(ctx context.Context) { _, err = git.Clone(storer, fs, cloneOptions) if err != nil { log.Printf("[WARNING] Failed loading repo %s into memory: %s", apis, err) - } else { + } else if err == nil && len(workflowapps) < 10 { log.Printf("[INFO] Finished git clone. Looking for updates to the repo.") dir, err := fs.ReadDir("") if err != nil { @@ -4228,6 +4230,8 @@ func runInitEs(ctx context.Context) { iterateOpenApiGithub(fs, dir, "", "") log.Printf("[INFO] Finished downloading extra API samples") + } else { + log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps)) } log.Printf("[INFO] Finished INIT (ES)") diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx new file mode 100644 index 00000000..bea2cc54 --- /dev/null +++ b/frontend/src/components/AppSelection.jsx @@ -0,0 +1,569 @@ +import React, { useState, useEffect, useRef } from "react"; +import theme from '../theme.jsx'; +import ReactGA from 'react-ga4'; +import { useNavigate, Link } from 'react-router-dom'; + +import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'; +import aa from 'search-insights' +import DeleteIcon from '@mui/icons-material/Delete'; +import ShowChartIcon from '@mui/icons-material/ShowChart'; +import ExploreIcon from '@mui/icons-material/Explore'; +import LightbulbIcon from "@mui/icons-material/Lightbulb"; +import NewReleasesIcon from "@mui/icons-material/NewReleases"; +import ExtensionIcon from "@mui/icons-material/Extension"; +import EmailIcon from "@mui/icons-material/Email"; +import FingerprintIcon from '@mui/icons-material/Fingerprint'; +import AppSearch from "../components/Appsearch.jsx"; +import { toast } from 'react-toastify'; +import { + Zoom, + Grid, + Paper, + TextField, + Collapse, + IconButton, + Avatar, + ButtonBase, + InputAdornment, + Typography, + Button, + Tooltip, + List, + ListItem, + ListItemAvatar, + ListItemText, +} from '@mui/material'; + +const AppSelection = props => { + const { + userdata, + globalUrl, + appFramework, + setActiveStep, + defaultSearch, + setDefaultSearch, + checkLogin, + } = props; + const [discoveryData, setDiscoveryData] = React.useState({}) + const [selectionOpen, setSelectionOpen] = React.useState(false) + const [newSelectedApp, setNewSelectedApp] = React.useState({}) + const [finishedApps, setFinishedApps] = React.useState([]) + const [moreButton, setMoreButton] = useState(false); + const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) + const ref = useRef() + let navigate = useNavigate(); + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + + const setFrameworkItem = (data) => { + console.log("Setting framework item: ", data, isCloud) + // if (!isCloud) { + // activateApp(data.id) + // } + + fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { + 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 framework!"); + } + + if (checkLogin !== undefined) { + checkLogin() + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + toast("Failed updating: " + responseJson.reason) + } else { + toast("Failed to update framework for your org.") + + } + } + + //setFrameworkLoaded(true) + //setFrameworkData(responseJson) + }) + .catch((error) => { + if (checkLogin !== undefined) { + checkLogin() + } + + toast(error.toString()); + //setFrameworkLoaded(true) + }) + } + + const onNodeSelect = (label) => { + // if (setDiscoveryWrapper !== undefined) { + // setDiscoveryWrapper({ id: label }); + // } + + if (isCloud) { + ReactGA.event({ + category: "welcome", + action: `click_${label}`, + label: "", + }); + } + setDiscoveryData(label) + setSelectionOpen(true) + setNewSelectedApp({}) + + setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase()) + }; + + useEffect(() => { + if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) { + return + } + const submitNewApp = { + description: newSelectedApp.description, + id: newSelectedApp.objectID, + large_image: newSelectedApp.image_url, + name: newSelectedApp.name, + type: discoveryData + } + if (discoveryData === "CASES") { + appFramework.cases = submitNewApp + } + else if (discoveryData === "SIEM") { + appFramework.siem = submitNewApp + } + else if (discoveryData === "ERADICATION") { + appFramework.eradication = submitNewApp + } + else if (discoveryData === "INTEL") { + appFramework.intel = submitNewApp + } + else if (discoveryData === "EMAIL") { + appFramework.email = submitNewApp + } + else if (discoveryData === "NETWORK") { + appFramework.network = submitNewApp + } + else if (discoveryData === "ASSETS") { + appFramework.assets = submitNewApp + } + else if (discoveryData === "IAM") { + appFramework.iam = submitNewApp + } + setFrameworkItem(submitNewApp); + + setSelectionOpen(false); + console.log("Selected app changed (effect)"); + + }, [newSelectedApp]); + + const sizing = 510; + const buttonWidth = 450; + const buttonMargin = 10; + const bottomButtonStyle = { + borderRadius: 200, + marginTop: moreButton ? 44 : "", + height: 51, + width: 464, + fontSize: 16, + // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", + background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", + padding: "16px 24px", + // top: 20, + // margin: "auto", + itemAlign: "center", + // marginLeft: "65px", + }; + const buttonStyle = { + flex: 1, + width: 224, + padding: 25, + margin: buttonMargin, + color: "var(--White-text, #F1F1F1)", + fontWeight: 400, + fontSize: 17, + background: "rgba(33, 33, 33, 1)", + textTransform: 'capitalize', + border: mouseHoverIndex ? "1px solid rgba(33, 33, 33, 1)" : "1px solid #f85a3e", + borderRadius: 8, + marginRight: 8, + }; + + return ( + +
    + {selectionOpen ? ( +
    +
    +
    + {discoveryData} +
    +
    + + { + setSelectionOpen(false) + }} + > + + + + + { + e.preventDefault(); + setSelectionOpen(false) + setDefaultSearch("") + const submitDeletedApp = { + "description": "", + "id": "remove", + "name": "", + "type": discoveryData + } + setFrameworkItem(submitDeletedApp) + setNewSelectedApp({}) + setTimeout(() => { + setDiscoveryData({}) + setFrameworkItem(submitDeletedApp) + setNewSelectedApp({}) + }, 1000) + }} + > + + + +
    +
    +
    + +
    + ) : null} + + Find your apps + + + Select the apps you work with and we will connect the for you. + + + {/*Find your integrations!*/} +
    { + setMouseHoverIndex() + }} onMouseOut={() => { + setMouseHoverIndex(-1) + }} + > + +
    +
    + + +
    +
    + + +
    + {moreButton ? ( +
    { + setMouseHoverIndex() + }} onMouseOut={() => { + setMouseHoverIndex(-1) + }} + > + + + +
    + ) + : +
    + +
    } + + {/* + What do you want to automate first ? + + { onNodeSelect("Email") }} />} + label="Email" + labelPlacement="Email" + /> + { onNodeSelect("SIEM") }} />} + label="SIEM" + labelPlacement="SIEM" + /> + { onNodeSelect("EDR") }} />} + label="EDR" + labelPlacement="EDR" + /> + + */} +
    +
    +
    + +
    + + ) +} + +export default AppSelection; diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index f74c79af..a0d7842f 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -387,6 +387,8 @@ const WorkflowTemplatePopup = (props) => { //setIsActive(!isActive) if (errorMessage !== "") { toast("Already failed to generate a workflow for this usecase. Please try again later or contact support@shuffler.io.") + + setModalOpen(true) } else if (isActive) { toast("Workflow already generated. Please try another workflow template!") @@ -418,7 +420,7 @@ const WorkflowTemplatePopup = (props) => { - + From aad43c913d07ca0772ffe111d93c27a192935581 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:06:23 +0530 Subject: [PATCH 104/160] fix: making the script functional --- functions/kubernetes/generate_certs.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/functions/kubernetes/generate_certs.sh b/functions/kubernetes/generate_certs.sh index 606ea2fa..b0ee1258 100755 --- a/functions/kubernetes/generate_certs.sh +++ b/functions/kubernetes/generate_certs.sh @@ -12,7 +12,17 @@ fi echo "Using node IP: $node_ip to generate SSL certs!" +mkdir -p certs + # Generate CA key openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/reg.key -x509 -days 365 -out certs/reg.crt -subj "/CN=$node_ip" +# generate a random string +random_string=$(openssl rand -hex 3) + +docker run -d -p 5000:5000 --restart=always --name "shuffle-local-registry-$random_string" \ + -v $(pwd)/certs:/certs \ + -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/reg.crt \ + -e REGISTRY_HTTP_TLS_KEY=/certs/reg.key \ + registry:2 From 7ad4e50c70a733cc78afa9c93ffb17b8ba978276 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:33:47 +0530 Subject: [PATCH 105/160] fix: making the script functional --- functions/kubernetes/generate_certs.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/functions/kubernetes/generate_certs.sh b/functions/kubernetes/generate_certs.sh index b0ee1258..6d2e996c 100755 --- a/functions/kubernetes/generate_certs.sh +++ b/functions/kubernetes/generate_certs.sh @@ -2,12 +2,14 @@ read -p "Are you using Minikube? (yes/no): " is_minikube +default_ip=$(ifconfig | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}') + if [ "$is_minikube" == "yes" ]; then node_ip=$(minikube ip) else - read -p "Enter your node IP (default is localhost): " custom_ip + read -p "Enter your node IP (default is $default_ip): " custom_ip # Use localhost as the default value - node_ip=${custom_ip:-localhost} + node_ip=${custom_ip:-$default_ip} fi echo "Using node IP: $node_ip to generate SSL certs!" @@ -20,9 +22,13 @@ openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/reg.key -x509 -days 36 # generate a random string random_string=$(openssl rand -hex 3) +echo "Starting docker registry with name shuffle-local-registry-$random_string.." + docker run -d -p 5000:5000 --restart=always --name "shuffle-local-registry-$random_string" \ -v $(pwd)/certs:/certs \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/reg.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/reg.key \ registry:2 +echo "Done!" + From 0b128776e1e347be1248acab12f5eb93d688b8d6 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:34:32 +0530 Subject: [PATCH 106/160] init: gitignore for k8s --- functions/kubernetes/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 functions/kubernetes/.gitignore diff --git a/functions/kubernetes/.gitignore b/functions/kubernetes/.gitignore new file mode 100644 index 00000000..a2661ad0 --- /dev/null +++ b/functions/kubernetes/.gitignore @@ -0,0 +1 @@ +certs/ \ No newline at end of file From 0a6c3042fa63b84a3cad958cf494be5a7277a093 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:36:39 +0530 Subject: [PATCH 107/160] fix: updating docs --- functions/kubernetes/kubernetes.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index 384a9082..f8b9d8e5 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -2,17 +2,16 @@ ### Prerequisites: - Clone the https://github.com/shuffle/shuffle repository using Git then, navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. -- [Running a Kubernetes cluster](https://kubernetes.io/docs/setup/). You can do that with either minikube or run the cluster locally. If you're running on minikube, make sure to have exposed the ports ```30007-30008```, which you can do by running minikube like this: ```minikube start --extra-config=apiserver.service-node-port-range=30007-30008``` +- [Running a Kubernetes cluster](https://kubernetes.io/docs/setup/). You can do that with either minikube or run the cluster locally. If you're running on minikube and you can't access the ports 30007-30008, you can use ```kubectl port-forward svc/frontend 3000:80 -n shuffle``` to access the frontend on port 3000 - Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. To setup a docker registry, if you have docker installed on one of your node run following commands. ``` - openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/reg.key -x509 -days 365 -out certs/reg.crt -subj "/CN=" - docker run -d -p 5000:5000 --restart=always --name shuffle-local-registry \ - -v $(pwd)/certs:/certs \ - -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/reg.crt \ - -e REGISTRY_HTTP_TLS_KEY=/certs/reg.key \ - registry:2 + chmod +x generate_certs.sh + ./generate_certs.sh ``` - - If you've used the above commands to set up a registry, you'll need to skip an SSL verification for your registry. If you're using Containerd as a runtime + +This will give you a NODE_IP which is you're local IP if you're not sure about what to use. + +- If you've used the above commands to set up a registry, you'll need to skip an SSL verification for your registry. If you're using Containerd as a runtime add the following lines in /etc/containerd/config.toml ``` [plugins."io.containerd.grpc.v1.cri".registry.mirrors.""] @@ -20,13 +19,10 @@ [plugins."io.containerd.grpc.v1.cri".registry.configs."".tls] insecure_skip_verify = true - ``` Step 1: Create a namespace called shuffle in a cluster by running ```kubectl create ns shuffle```. -Step 2: Open the ```all-in-one.yaml``` file and review the configuration values. Change the value of REGISTRY_URL with ':5000' where the registry is at. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap with ```kubectl apply -f shuffle-cm.yaml -n shuffle```. - -Step 3: Now, Do ```kubectl apply -f all-in-one.yaml -n shuffle```. This will deploy all the necessary components of Shuffle! +Step 2: Open the ```all-in-one.yaml``` file and review the configuration values. Change the value of REGISTRY_URL with ':5000' where the registry is at. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap and deploy with ```kubectl apply -f all-in-one.yaml -n shuffle``` Now, open ```https://:30008``` or ```http://:30007```. You should be seeing a signup page. NODE_IP should be where the frontend is deployed. From 96261811fc716057ef30bce16c6cc1844aa01027 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:43:35 +0530 Subject: [PATCH 108/160] fix: defining configs --- functions/kubernetes/kubernetes.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index f8b9d8e5..243d24d5 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -11,6 +11,8 @@ This will give you a NODE_IP which is you're local IP if you're not sure about what to use. +- 8 GB RAM and 4 CPUs are recommended as **minimum configs** for running Shuffle on Kubernetes. K8s is a resource-intensive application, and you may experience performance issues if you run it on a machine with fewer resources. + - If you've used the above commands to set up a registry, you'll need to skip an SSL verification for your registry. If you're using Containerd as a runtime add the following lines in /etc/containerd/config.toml ``` @@ -20,7 +22,7 @@ This will give you a NODE_IP which is you're local IP if you're not sure about w [plugins."io.containerd.grpc.v1.cri".registry.configs."".tls] insecure_skip_verify = true ``` -Step 1: Create a namespace called shuffle in a cluster by running ```kubectl create ns shuffle```. +Step 1: Create a namespace called shuffle in a cluster by running ```kubectl create ns shuffle```. Step 2: Open the ```all-in-one.yaml``` file and review the configuration values. Change the value of REGISTRY_URL with ':5000' where the registry is at. Adjust other variables as per your deployment requirements; otherwise, the application will deploy using the default settings provided within the file. Then apply the configmap and deploy with ```kubectl apply -f all-in-one.yaml -n shuffle``` From 5a33135dd70f33ed5f51be7d9dbb9d09a2943c96 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 19 Oct 2023 14:19:34 +0200 Subject: [PATCH 109/160] Changed go.mod slightly --- .env | 3 ++- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 1 - frontend/src/components/WorkflowTemplatePopup.jsx | 7 ++++++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.env b/.env index 2b8a6a44..3fcb68b8 100755 --- a/.env +++ b/.env @@ -69,11 +69,12 @@ SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500 # 1500 by default # Used for auto-cleanup of containers. REALLY important at scale. Set to false to see all container info. SHUFFLE_CONTAINER_AUTO_CLEANUP=true SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY=3 # The amount of concurrent executions Orborus can handle. This is a soft limit, but it's recommended to keep it low. +SHUFFLE_HEALTHCHECK_DISABLED=false SHUFFLE_ELASTIC=true SHUFFLE_LOGS_DISABLED=false SHUFFLE_CHAT_DISABLED=false # Controls support chat -SHUFFLE_RERUN_SCHEDULE=300 SHUFFLE_DISABLE_RERUN_AND_ABORT=false +SHUFFLE_RERUN_SCHEDULE=300 SHUFFLE_WORKER_SERVER_URL= # Definition in case Worker & Orborus is talking to the wrong server SHUFFLE_ORBORUS_PULL_TIME= # Definition in case Orborus is pulling too often/not often enough diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 109796ed..e5cc3345 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared go 1.19 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ba444b10..c1256808 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5861,7 +5861,6 @@ func initHandlers() { r := mux.NewRouter() r.HandleFunc("/api/v1/_ah/health", shuffle.HealthCheckHandler) r.HandleFunc("/api/v1/health", shuffle.RunOpsHealthCheck).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/health/stats", shuffle.GetOpsDashboardStats).Methods("GET", "OPTIONS") // Make user related locations diff --git a/frontend/src/components/WorkflowTemplatePopup.jsx b/frontend/src/components/WorkflowTemplatePopup.jsx index a0d7842f..f1367092 100644 --- a/frontend/src/components/WorkflowTemplatePopup.jsx +++ b/frontend/src/components/WorkflowTemplatePopup.jsx @@ -171,8 +171,13 @@ const WorkflowTemplatePopup = (props) => { // name: "Email analysis" // source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name - setWorkflowLoading(true) + + if (srcapp.includes(":default") || dstapp.includes(":default")) { + toast("You need to select both a source and destination app before generating this workflow.") + return + } + setWorkflowLoading(true) // FIXME: Remove hardcoding here after testing, and user srcapp/dstapp const newsrcapp = srcapp From 0dc85776b8bd5c988ab0793e7bd4ccb82faf544f Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:07:42 +0530 Subject: [PATCH 110/160] fix: adding correct images --- functions/kubernetes/all-in-one.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/kubernetes/all-in-one.yaml b/functions/kubernetes/all-in-one.yaml index be83725b..84714af7 100644 --- a/functions/kubernetes/all-in-one.yaml +++ b/functions/kubernetes/all-in-one.yaml @@ -66,7 +66,7 @@ data: IS_KUBERNETES: "true" REGISTRY_URL: "172.17.14.71:5000" REGISTRY_AUTH: "false" - SHUFFLE_KUBERNETES_WORKER: "dhavald055/shuffle-k8s:shuffle-worker-kubernetes" + SHUFFLE_KUBERNETES_WORKER: "shuffle/shuffle:shuffle-worker" kind: ConfigMap metadata: creationTimestamp: null @@ -623,7 +623,7 @@ spec: configMapKeyRef: key: REGISTRY_AUTH name: env - image: dhavald055/shuffle-k8s:shuffle-backend-kubernetes + image: shuffle/shuffle:shuffle-backend imagePullPolicy: Always name: shuffle-backend ports: @@ -690,7 +690,7 @@ spec: containers: - env: - name: BACKEND_HOSTNAME - image: ghcr.io/shuffle/shuffle-frontend:latest + image: ghcr.io/shuffle/shuffle-frontend:nightly name: shuffle-frontend ports: - containerPort: 80 @@ -790,7 +790,7 @@ spec: key: SHUFFLE_KUBERNETES_WORKER name: env - image: dhavald055/shuffle-k8s:shuffle-orborus-kubernetes + image: shuffle/shuffle:shuffle-orborus #imagePullPolicy: Never name: shuffle-orborus resources: {} From 13fd60a167d009f3d86ee6ed43f79dd8050aff12 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:10:41 +0530 Subject: [PATCH 111/160] fix: adding correct images --- functions/kubernetes/all-in-one.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/kubernetes/all-in-one.yaml b/functions/kubernetes/all-in-one.yaml index 84714af7..7b657456 100644 --- a/functions/kubernetes/all-in-one.yaml +++ b/functions/kubernetes/all-in-one.yaml @@ -66,7 +66,7 @@ data: IS_KUBERNETES: "true" REGISTRY_URL: "172.17.14.71:5000" REGISTRY_AUTH: "false" - SHUFFLE_KUBERNETES_WORKER: "shuffle/shuffle:shuffle-worker" + SHUFFLE_KUBERNETES_WORKER: "shuffle/shuffle:shuffle-worker:1.3.0" kind: ConfigMap metadata: creationTimestamp: null @@ -623,7 +623,7 @@ spec: configMapKeyRef: key: REGISTRY_AUTH name: env - image: shuffle/shuffle:shuffle-backend + image: shuffle/shuffle:shuffle-backend:1.3.0 imagePullPolicy: Always name: shuffle-backend ports: @@ -690,7 +690,7 @@ spec: containers: - env: - name: BACKEND_HOSTNAME - image: ghcr.io/shuffle/shuffle-frontend:nightly + image: ghcr.io/shuffle/shuffle-frontend::1.3.0 name: shuffle-frontend ports: - containerPort: 80 @@ -790,7 +790,7 @@ spec: key: SHUFFLE_KUBERNETES_WORKER name: env - image: shuffle/shuffle:shuffle-orborus + image: shuffle/shuffle:shuffle-orborus:1.3.0 #imagePullPolicy: Never name: shuffle-orborus resources: {} From ce543ceafbf21732f3a3764f74d4d8f73256c22d Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:13:04 +0530 Subject: [PATCH 112/160] fix: adding correct images --- functions/kubernetes/all-in-one.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/kubernetes/all-in-one.yaml b/functions/kubernetes/all-in-one.yaml index 7b657456..94f7bfd0 100644 --- a/functions/kubernetes/all-in-one.yaml +++ b/functions/kubernetes/all-in-one.yaml @@ -690,7 +690,7 @@ spec: containers: - env: - name: BACKEND_HOSTNAME - image: ghcr.io/shuffle/shuffle-frontend::1.3.0 + image: ghcr.io/shuffle/shuffle-frontend:1.3.0 name: shuffle-frontend ports: - containerPort: 80 From 5d0b7c217ebd81222eb7f3102f8c163e99dd6dca Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:43:52 +0530 Subject: [PATCH 113/160] fix: making docs better --- functions/kubernetes/kubernetes.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/functions/kubernetes/kubernetes.md b/functions/kubernetes/kubernetes.md index 243d24d5..b6a2c60e 100644 --- a/functions/kubernetes/kubernetes.md +++ b/functions/kubernetes/kubernetes.md @@ -2,7 +2,7 @@ ### Prerequisites: - Clone the https://github.com/shuffle/shuffle repository using Git then, navigate to the functions/kubernetes directory, which contains all the necessary Kubernetes configuration files for deployment. -- [Running a Kubernetes cluster](https://kubernetes.io/docs/setup/). You can do that with either minikube or run the cluster locally. If you're running on minikube and you can't access the ports 30007-30008, you can use ```kubectl port-forward svc/frontend 3000:80 -n shuffle``` to access the frontend on port 3000 +- [Running a Kubernetes cluster](https://kubernetes.io/docs/setup/). You can do that with either minikube or run the cluster locally. - Ensure you have a local Docker registry set up to store and manage Docker images for applications built with Shuffle. While the registry is crucial for handling custom-built apps, you’ll still be able to run workflows without it. To setup a docker registry, if you have docker installed on one of your node run following commands. ``` chmod +x generate_certs.sh @@ -28,3 +28,5 @@ Step 2: Open the ```all-in-one.yaml``` file and review the configuration values. Now, open ```https://:30008``` or ```http://:30007```. You should be seeing a signup page. NODE_IP should be where the frontend is deployed. +If you're running on minikube and you can't access the ports 30007-30008, you can use ```kubectl port-forward svc/frontend 3001:80 -n shuffle``` to access the frontend on port 3000 + From 3a6d4c5f3974bead1f8a19cca745b17eab71bc0e Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:53:16 +0530 Subject: [PATCH 114/160] fix: go.mod fix --- backend/go-app/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 109796ed..013c1b65 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -1,6 +1,6 @@ module shuffle-shared -replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared +// replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared go 1.19 From da5cc1d690c570d0e105f944267194a6da92ab11 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Thu, 19 Oct 2023 20:18:14 +0530 Subject: [PATCH 115/160] fix: go.mod --- backend/go-app/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 9ce62203..f698ae30 100755 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -20,7 +20,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.4.54 + github.com/shuffle/shuffle-shared v0.4.55 golang.org/x/crypto v0.9.0 google.golang.org/api v0.125.0 google.golang.org/appengine v1.6.7 From 74dfa09d4cab529612916f8a6cf0dff76721e9c7 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 19 Oct 2023 17:22:19 +0200 Subject: [PATCH 116/160] Further health fixes and frontend cleanup --- backend/go-app/main.go | 37 +++- frontend/src/components/AppSelection.jsx | 208 ++++++++++++-------- frontend/src/components/Appsearch.jsx | 13 +- frontend/src/components/ExploreWorkflow.jsx | 2 +- frontend/src/components/Priority.jsx | 4 +- frontend/src/components/WelcomeForm2.jsx | 85 +------- frontend/src/views/AngularWorkflow.jsx | 2 +- frontend/src/views/Welcome.jsx | 8 +- 8 files changed, 174 insertions(+), 185 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index c1256808..63f20748 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -27,6 +27,9 @@ import ( "os/exec" "path/filepath" + // import httptest + "net/http/httptest" + //"regexp" "strings" "time" @@ -4025,7 +4028,7 @@ func runInitEs(ctx context.Context) { jobret, err := newscheduler.Every(int(interval)).Seconds().NotImmediately().Run(job) if err != nil { - log.Printf("[CRITICAL] Failed to schedule org: %s", err) + log.Printf("[ERROR] Failed to schedule org: %s", err) } else { log.Printf("[INFO] Started sync on interval %d for org %s (%s)", interval, org.Name, org.Id) scheduledOrgs[org.Id] = jobret @@ -4234,6 +4237,32 @@ func runInitEs(ctx context.Context) { log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps)) } + + if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" { + healthcheckInterval := 15 + log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval) + job := func() { + // Prepare a fake http.responsewriter + resp := httptest.NewRecorder() + + request := http.Request{} + // Add the "force=true" query to the fake request + request.URL, err = url.Parse("/api/v1/health/stats?force=true") + if err != nil { + log.Printf("[ERROR] Failed to parse test url for healthstats: %s", err) + } + + shuffle.RunOpsHealthCheck(resp, &request) + } + + _, err := newscheduler.Every(int(healthcheckInterval)).Minutes().Run(job) + if err != nil { + log.Printf("[ERROR] Failed to schedule healthcheck: %s", err) + } else { + log.Printf("[DEBUG] Successfully started healthcheck interval of %d minutes", healthcheckInterval) + } + } + log.Printf("[INFO] Finished INIT (ES)") } @@ -4731,9 +4760,9 @@ func runInit(ctx context.Context) { jobret, err := newscheduler.Every(int(interval)).Seconds().NotImmediately().Run(job) if err != nil { - log.Printf("[CRITICAL] Failed to schedule org: %s", err) + log.Printf("[ERROR] Failed to schedule org: %s", err) } else { - log.Printf("Started sync on interval %d for org %s", interval, org.Name) + log.Printf("[INFO] Started sync on interval %d for org %s", interval, org.Name) scheduledOrgs[org.Id] = jobret } } @@ -5281,7 +5310,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { jobret, err := newscheduler.Every(int(interval)).Seconds().NotImmediately().Run(job) if err != nil { - log.Printf("[CRITICAL] Failed to schedule org: %s", err) + log.Printf("[ERROR] Failed to schedule org: %s", err) } else { log.Printf("[INFO] Started sync on interval %d for org %s", interval, org.Name) scheduledOrgs[org.Id] = jobret diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index bea2cc54..5ead4f9a 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -48,12 +48,20 @@ const AppSelection = props => { const [selectionOpen, setSelectionOpen] = React.useState(false) const [newSelectedApp, setNewSelectedApp] = React.useState({}) const [finishedApps, setFinishedApps] = React.useState([]) + const [appName, setAppName] = React.useState(); const [moreButton, setMoreButton] = useState(false); - const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) + + // const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) const ref = useRef() let navigate = useNavigate(); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; + const mouseOver = (e) => { + e.target.style.border = "1px solid #f85a3e"; + } + const mouseOut = (e) => { + e.target.style.border = "1px solid rgb(33, 33, 33)"; + } const setFrameworkItem = (data) => { console.log("Setting framework item: ", data, isCloud) // if (!isCloud) { @@ -89,7 +97,6 @@ const AppSelection = props => { } } - //setFrameworkLoaded(true) //setFrameworkData(responseJson) }) @@ -118,7 +125,6 @@ const AppSelection = props => { setDiscoveryData(label) setSelectionOpen(true) setNewSelectedApp({}) - setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase()) }; @@ -140,13 +146,13 @@ const AppSelection = props => { appFramework.siem = submitNewApp } else if (discoveryData === "ERADICATION") { - appFramework.eradication = submitNewApp + appFramework.edr = submitNewApp } else if (discoveryData === "INTEL") { appFramework.intel = submitNewApp } else if (discoveryData === "EMAIL") { - appFramework.email = submitNewApp + appFramework.communication = submitNewApp } else if (discoveryData === "NETWORK") { appFramework.network = submitNewApp @@ -158,27 +164,27 @@ const AppSelection = props => { appFramework.iam = submitNewApp } setFrameworkItem(submitNewApp); - setSelectionOpen(false); console.log("Selected app changed (effect)"); - }, [newSelectedApp]); - const sizing = 510; - const buttonWidth = 450; - const buttonMargin = 10; + const sizing = moreButton ? 510 : 480; + const buttonWidth = 450; + const buttonMargin = 10; const bottomButtonStyle = { borderRadius: 200, marginTop: moreButton ? 44 : "", height: 51, - width: 464, + width: 510, fontSize: 16, // background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)", background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)", padding: "16px 24px", // top: 20, // margin: "auto", + textTransform: 'capitalize', itemAlign: "center", + // marginTop: 25 // marginLeft: "65px", }; const buttonStyle = { @@ -191,18 +197,18 @@ const AppSelection = props => { fontSize: 17, background: "rgba(33, 33, 33, 1)", textTransform: 'capitalize', - border: mouseHoverIndex ? "1px solid rgba(33, 33, 33, 1)" : "1px solid #f85a3e", + border: "1px solid rgba(33, 33, 33, 1)", borderRadius: 8, marginRight: 8, }; - + // console.log("appFramework",appFramework.cases.name) return (
    @@ -223,8 +229,8 @@ const AppSelection = props => { }} >
    -
    - {discoveryData} +
    + {discoveryData}
    { setFrameworkItem(submitDeletedApp) setNewSelectedApp({}) }, 1000) + //setAppName(discoveryData.cases.name) }} > @@ -319,14 +326,9 @@ const AppSelection = props => { Select the apps you work with and we will connect the for you. + {/*Find your integrations!*/} -
    { - setMouseHoverIndex() - }} onMouseOut={() => { - setMouseHoverIndex(-1) - }} - > +
    -
    +
    -
    +
    {moreButton ? ( -
    { - setMouseHoverIndex() - }} onMouseOut={() => { - setMouseHoverIndex(-1) - }} - > +
    ) : -
    +
    } - - {/* - What do you want to automate first ? - - { onNodeSelect("Email") }} />} - label="Email" - labelPlacement="Email" - /> - { onNodeSelect("SIEM") }} />} - label="SIEM" - labelPlacement="SIEM" - /> - { onNodeSelect("EDR") }} />} - label="EDR" - labelPlacement="EDR" - /> - - */}
    diff --git a/frontend/src/components/Appsearch.jsx b/frontend/src/components/Appsearch.jsx index 87851541..1879d0ef 100644 --- a/frontend/src/components/Appsearch.jsx +++ b/frontend/src/components/Appsearch.jsx @@ -54,14 +54,14 @@ const Appsearch = props => { @@ -71,7 +71,8 @@ const Appsearch = props => { type="search" color="primary" defaultValue={defaultSearch} - placeholder={`Find ${defaultSearch} Apps...`} + // placeholder={`Find ${defaultSearch} Apps...`} + placeholder= {defaultSearch ? `${defaultSearch}` : "Search Cases "} id="shuffle_workflow_search_field" onChange={(event) => { refine(event.currentTarget.value) @@ -92,9 +93,9 @@ const Appsearch = props => { {hits.map((data, index) => { const paperStyle = { - backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor, + backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : "#2F2F2F", color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)", - border: newSelectedApp.objectID !== data.objectID ? `1px solid rgba(255,255,255,0.2)` : "2px solid #f86a3e", + // border: newSelectedApp.objectID !== data.objectID ? `1px solid rgba(255,255,255,0.2)` : "2px solid #f86a3e", textAlign: "left", padding: 10, cursor: "pointer", @@ -183,7 +184,7 @@ const Appsearch = props => { } }}>
    - {data.name} + {data.name} {parsedname} diff --git a/frontend/src/components/ExploreWorkflow.jsx b/frontend/src/components/ExploreWorkflow.jsx index fc3c2d32..95b464e7 100644 --- a/frontend/src/components/ExploreWorkflow.jsx +++ b/frontend/src/components/ExploreWorkflow.jsx @@ -295,7 +295,7 @@ const ExploreWorkflow = (props) => { Start using workflows - Based on what you selected here’s our recommendations! + Based on what you selected workflows, here are our recommendations! You will see more of these later.
    diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index c81086e8..3f167982 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -112,7 +112,7 @@ const Priority = (props) => { {priority.type === "usecase" && priority.description.includes("&") ? - {priority.name} + {priority.name} {newdescription.split("&")[0]} @@ -120,7 +120,7 @@ const Priority = (props) => { {newdescription.split("&").length > 3 ? - {priority.name+"2"} + {priority.name+"2"} {newdescription.split("&")[2]} diff --git a/frontend/src/components/WelcomeForm2.jsx b/frontend/src/components/WelcomeForm2.jsx index b2f40193..f0b71273 100644 --- a/frontend/src/components/WelcomeForm2.jsx +++ b/frontend/src/components/WelcomeForm2.jsx @@ -200,20 +200,10 @@ const WelcomeForm = (props) => { }, [discoveryWrapper]); useEffect(() => { - if ( - window.location.search !== undefined && - window.location.search !== null - ) { + if (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 && !isNaN(foundTab)) { - if (foundTab === 3 || foundTab === "3") { - //console.log("Set search!") - } - } else { - //navigate(`/welcome?tab=1`) - } const foundTemplate = params["workflow_template"]; if (foundTemplate !== null && foundTemplate !== undefined) { @@ -730,77 +720,6 @@ const WelcomeForm = (props) => { ) : (
    {getStepContent(activeStep)} - {/*
    - {activeStep === 2 || activeStep === 1 ? -
    - - -
    - : -
    - - // (commented) isStepOptional(activeStep) && ( - - ) //commented - - {activeStep === 0 ? - - : null} -
    - }*/}
    )}
    diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 1dd3eb87..55051fe7 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -13321,7 +13321,7 @@ const AngularWorkflow = (defaultprops) => { - {workflow.errors.slice(0,4).map((error) => { + {workflow.errors.slice(0,3).map((error) => { return (
    - {error} diff --git a/frontend/src/views/Welcome.jsx b/frontend/src/views/Welcome.jsx index 7683f826..97fed9f4 100644 --- a/frontend/src/views/Welcome.jsx +++ b/frontend/src/views/Welcome.jsx @@ -42,7 +42,6 @@ const Welcome = (props) => { let navigate = useNavigate(); useEffect(() => { - console.log("IN USEEFFECT FOR ACTIVESTEP: ", activeStep, " and with login: ", checkLogin) if (checkLogin !== undefined) { checkLogin() } @@ -280,7 +279,7 @@ const Welcome = (props) => { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); const foundTab = params["tab"]; - if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab)) { + if (foundTab !== null && foundTab !== undefined && !isNaN(foundTab) && foundTab >= 1 && foundTab <= 3) { setShowWelcome(true) if (foundTab === 3 || foundTab === "3") { handleSetSearch(usecaseButtons[0].name, usecaseButtons[0].usecase) @@ -288,8 +287,7 @@ const Welcome = (props) => { setActiveStep(foundTab-1) } else { - //navigate(`/welcome?tab=1`) - navigate(`/welcome?tab=2`) + navigate(`/welcome?tab=2`) } } }, []) @@ -528,6 +526,8 @@ const Welcome = (props) => {
    +
    +
    + + +
    +
    + + +
    + {moreButton ? ( +
    + + + +
    + ) + : +
    + +
    } */} +
    +
    } + onClick={(event) => { + onNodeSelect("CASES"); + setDefaultSearch(discoveryData.label) + }} + > +
    + {AppImage === undefined || AppImage === undefined || + AppImage === null || AppImage === null || AppImage.length === 0 ? +
    + +
    + : } +
    + Case Management + {appName === undefined || appName === undefined || + appName === null || appName === null || appName.length === 0 ? + "" + : + {appName}} +
    + +
    +
    +
    + + ) +} +export default AppSearchButtons diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index 5ead4f9a..d5f212df 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -14,6 +14,7 @@ import ExtensionIcon from "@mui/icons-material/Extension"; import EmailIcon from "@mui/icons-material/Email"; import FingerprintIcon from '@mui/icons-material/Fingerprint'; import AppSearch from "../components/Appsearch.jsx"; +import AppSearchButtons from "../components/AppSearchButtons.jsx"; import { toast } from 'react-toastify'; import { Zoom, @@ -42,26 +43,23 @@ const AppSelection = props => { setActiveStep, defaultSearch, setDefaultSearch, - checkLogin, + checkLogin, } = props; const [discoveryData, setDiscoveryData] = React.useState({}) const [selectionOpen, setSelectionOpen] = React.useState(false) const [newSelectedApp, setNewSelectedApp] = React.useState({}) const [finishedApps, setFinishedApps] = React.useState([]) + const [appButtons, setAppButtons] = useState([]) + const [apps, setApps] = useState([]) const [appName, setAppName] = React.useState(); const [moreButton, setMoreButton] = useState(false); - + // const [mouseHoverIndex, setMouseHoverIndex] = useState(-1) const ref = useRef() let navigate = useNavigate(); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - const mouseOver = (e) => { - e.target.style.border = "1px solid #f85a3e"; - } - const mouseOut = (e) => { - e.target.style.border = "1px solid rgb(33, 33, 33)"; - } + const setFrameworkItem = (data) => { console.log("Setting framework item: ", data, isCloud) // if (!isCloud) { @@ -82,9 +80,9 @@ const AppSelection = props => { console.log("Status not 200 for framework!"); } - if (checkLogin !== undefined) { - checkLogin() - } + if (checkLogin !== undefined) { + checkLogin() + } return response.json(); }) @@ -101,14 +99,55 @@ const AppSelection = props => { //setFrameworkData(responseJson) }) .catch((error) => { - if (checkLogin !== undefined) { - checkLogin() - } + if (checkLogin !== undefined) { + checkLogin() + } toast(error.toString()); //setFrameworkLoaded(true) }) } + const GetApps = (data) => { + console.log("Setting framework item: ", data, isCloud) + // if (!isCloud) { + // activateApp(data.id) + // } + + fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(data), + credentials: "include", + }) + .then((responseJson) => { + if (responseJson === null) { + console.log("null-response from server") + const pretend_apps = [{ + "description": "TBD", + "id": "TBD", + "large_image": "", + "name": "TBD", + "type": "TBD" + }] + + setApps(pretend_apps) + return + } + + if (responseJson.success === false) { + console.log("error loading apps: ", responseJson) + return + } + + setApps(responseJson); + }) + .catch((error) => { + console.log("App loading error: " + error.toString()); + }) + } const onNodeSelect = (label) => { // if (setDiscoveryWrapper !== undefined) { @@ -128,6 +167,36 @@ const AppSelection = props => { setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase()) }; + useEffect(() => { + var tempApps = [] + if (tempApps.length === 0) { + const tempApps = + [{ + "description": newSelectedApp.description, + "id": newSelectedApp.objectID, + "large_image": newSelectedApp.image_url, + "name": newSelectedApp.name, + "type": discoveryData + }, + //{ + // // description: newSelectedApp.siem.description, + // id: newSelectedApp.siem.objectID, + // large_image: newSelectedApp.siem.image_url, + // name: newSelectedApp.siem.name, + // type: discoveryData.siem + // },{ + // // description: newSelectedApp.edr.description, + // id: newSelectedApp.edr.objectID, + // large_image: newSelectedApp.edr.image_url, + // name: newSelectedApp.edr.name, + // type: discoveryData.edr + // } + ] + setAppButtons(tempApps) + GetApps() + } + }, []) + useEffect(() => { if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) { return @@ -169,7 +238,7 @@ const AppSelection = props => { }, [newSelectedApp]); const sizing = moreButton ? 510 : 480; - const buttonWidth = 450; + const buttonWidth = 450; const buttonMargin = 10; const bottomButtonStyle = { borderRadius: 200, @@ -186,7 +255,7 @@ const AppSelection = props => { itemAlign: "center", // marginTop: 25 // marginLeft: "65px", - }; + }; const buttonStyle = { flex: 1, width: 224, @@ -229,8 +298,8 @@ const AppSelection = props => { }} >
    -
    - {discoveryData} +
    + {discoveryData}
    { > Select the apps you work with and we will connect the for you. - - - {/*Find your integrations!*/} -
    - -
    -
    - - -
    -
    - - -
    - {moreButton ? ( -
    - - - -
    + {appButtons.map((appData, index) => { + + const appName = appData.name + const AppImage = appData.large_image + const appType = appData.type + + return ( + + ) - : -
    - -
    } -
    + })}
    {/* From 8914acdca634d3854dee2d5759f76aaf51d4787f Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Mon, 30 Oct 2023 02:38:57 +0530 Subject: [PATCH 158/160] fix: color fix --- frontend/src/views/Workflows.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index d3821312..67998211 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -3473,7 +3473,7 @@ const Workflows = (props) => {
    - {/* From 859a8b39249f6a6debee8a6b8512aa5c9034b3d3 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Mon, 30 Oct 2023 07:05:43 +0530 Subject: [PATCH 159/160] fix: adding debug memory mode --- .env | 4 +++- functions/onprem/orborus/orborus.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 0dca8870..e92d1ba0 100755 --- a/.env +++ b/.env @@ -3,7 +3,7 @@ ORG_ID=Shuffle ENVIRONMENT_NAME=Shuffle # Sanitize liquid.py input -LIQUID_SANITIZE_INPUT=true +LIQUID_SANITIZE_INPUT=false # Remote github config for first load @@ -92,3 +92,5 @@ SHUFFLE_OPENSEARCH_CLOUDID= SHUFFLE_OPENSEARCH_PROXY= SHUFFLE_OPENSEARCH_INDEX_PREFIX= SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY=true + +DEBUG_MODE=false \ No newline at end of file diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index cf8205f0..8b6846c2 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -465,6 +465,7 @@ func deployServiceWorkers(image string) { fmt.Sprintf("SHUFFLE_APP_REPLICAS=%d", cnt), fmt.Sprintf("TZ=%s", timezone), fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")), + fmt.Sprintf("DEBUG_MEMORY=%s", os.Getenv("DEBUG_MEMORY")), }, //Hosts: []string{ // innerContainerName, From f4813e0f0395e5c3b06d4f010091978cf633a9c9 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Mon, 30 Oct 2023 07:08:05 +0530 Subject: [PATCH 160/160] fix: LIQUID_SANITIZE_INPUT=true --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index e92d1ba0..f8be5532 100755 --- a/.env +++ b/.env @@ -3,7 +3,7 @@ ORG_ID=Shuffle ENVIRONMENT_NAME=Shuffle # Sanitize liquid.py input -LIQUID_SANITIZE_INPUT=false +LIQUID_SANITIZE_INPUT=true # Remote github config for first load