From 8232dbf2e1ccd0529f3e7b7b32c4d62710d1c8f2 Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 6 Nov 2020 16:42:21 +0100 Subject: [PATCH] Added basic organization edit page --- backend/go-app/main.go | 76 ++++++- backend/go-app/walkoff.go | 15 +- frontend/src/App.jsx | 2 +- frontend/src/views/Admin.jsx | 345 ++++++++++++++++++++++++++++--- functions/onprem/worker/build.sh | 2 +- 5 files changed, 394 insertions(+), 46 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 93cb03be..413d3ec8 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -2298,6 +2298,70 @@ func handleGetEnvironments(resp http.ResponseWriter, request *http.Request) { resp.Write(newjson) } +func handleGetOrg(resp http.ResponseWriter, request *http.Request) { + cors := handleCors(resp, request) + if cors { + return + } + + var fileId string + location := strings.Split(request.URL.String(), "/") + if location[1] == "api" { + if len(location) <= 4 { + log.Printf("Path too short: %d", len(location)) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + fileId = location[4] + } + + user, err := handleApiAuthentication(resp, request) + if err != nil { + log.Printf("Api authentication failed in set new workflowhandler: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + ctx := context.Background() + org, err := getOrg(ctx, fileId) + if err != nil { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Failed getting org users"}`)) + return + } + + //FIXME : cleanup org before marshal + userFound := false + for _, foundUser := range org.Users { + if foundUser.Id == user.Id { + userFound = true + break + } + } + + if !userFound { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Use doesn't have access to org"}`)) + return + } + + org.Users = []User{} + org.SyncConfig.Apikey = "" + newjson, err := json.Marshal(org) + if err != nil { + log.Printf("Failed unmarshal of org: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking"}`))) + return + } + + resp.WriteHeader(200) + resp.Write(newjson) +} + func handleGetOrgs(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { @@ -2312,7 +2376,7 @@ func handleGetOrgs(resp http.ResponseWriter, request *http.Request) { return } - if user.Role != "admin" { + if user.Role != "global_admin" { resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Not admin"}`)) return @@ -3507,7 +3571,7 @@ func executeCloudAction(action CloudSyncJob, apikey string) error { Reason string `json:"reason"` } - log.Printf("Data: %s", string(respBody)) + //log.Printf("Data: %s", string(respBody)) responseData := Result{} err = json.Unmarshal(respBody, &responseData) if err != nil { @@ -3554,7 +3618,7 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) { return } - log.Printf("Data: %s", string(body)) + //log.Printf("Data: %s", string(body)) ctx := context.Background() var requestdata requestData @@ -6743,7 +6807,7 @@ func remoteOrgJobHandler(org Org, interval int) error { req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey)) newresp, err := client.Do(req) if err != nil { - log.Printf("Failed request in org sync: %s", err) + //log.Printf("Failed request in org sync: %s", err) return err } @@ -6753,7 +6817,7 @@ func remoteOrgJobHandler(org Org, interval int) error { return err } - log.Printf("Data: %s", respBody) + //log.Printf("Data: %s", respBody) err = remoteOrgJobController(org, respBody) if err != nil { @@ -7958,6 +8022,8 @@ func initHandlers() { // NEW for 0.8.0 r.HandleFunc("/api/v1/cloud/setup", handleCloudSetup).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs", handleGetOrgs).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/orgs/{orgId}", handleGetOrg).Methods("GET", "OPTIONS") + //r.HandleFunc("/api/v1/orgs/{orgId}", handleEditOrg).Methods("POST", "OPTIONS") // Important for email, IDS etc. Create this by: // PS: For cloud, this has to use cloud storage. diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index ad742389..6ce5a77b 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -68,19 +68,22 @@ type ExecutionRequest struct { } type SyncFeatures struct { + Webhook SyncData `json:"webhook" datastore:"webhook"` + Schedule SyncData `json:"schedule" datastore:"schedule"` + UserInput SyncData `json:"user_input" datastore:"user_input"` + EmailTrigger SyncData `json:"email_trigger" datastore:"email_trigger"` Apps SyncData `json:"apps" datastore:"apps"` Workflows SyncData `json:"workflows" datastore:"workflows"` Schedules SyncData `json:"schedules" datastore:"schedules"` Autocomplete SyncData `json:"autocomplete" datastore:"autocomplete"` Authentication SyncData `json:"authentication" datastore:"authentication"` - Webhook SyncData `json:"webhook" datastore:"webhook"` - Schedule SyncData `json:"schedule" datastore:"schedule"` - UserInput SyncData `json:"user_input" datastore:"user_input"` - EmailTrigger SyncData `json:"email_trigger" datastore:"email_trigger"` } type SyncData struct { - Active bool `json:"active" datastore:"active"` + Active bool `json:"active" datastore:"active"` + Name string `json:"name" datastore:"name"` + Description string `json:"description" datastore:"description"` + Limit int64 `json:"limit" datastore:"limit"` } type SyncConfig struct { @@ -91,6 +94,8 @@ type SyncConfig struct { // Role is just used for feedback for a user type Org struct { Name string `json:"name" datastore:"name"` + Description string `json:"description" datastore:"description"` + Image string `json:"image" datastore:"image"` Id string `json:"id" datastore:"id"` Org string `json:"org" datastore:"org"` Users []User `json:"users" datastore:"users"` diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 8e57b486..16c2ca17 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -118,7 +118,7 @@ const App = (message, props) => { const options = { timeout: 5000, - position: positions.TOP_CENTER, + position: positions.BOTTOM_LEFT, }; const includedData = window.location.pathname === "/home" || window.location.pathname === "/features" ? diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index d3deb492..817a20e2 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -1,7 +1,10 @@ import React, { useEffect} from 'react'; +import { makeStyles } from '@material-ui/styles'; import {Link} from 'react-router-dom'; import Paper from '@material-ui/core/Paper'; +import Card from '@material-ui/core/Card'; +import Tooltip from '@material-ui/core/Tooltip'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Typography from '@material-ui/core/Typography'; import Switch from '@material-ui/core/Switch'; @@ -20,6 +23,7 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar'; import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; import IconButton from '@material-ui/core/IconButton'; import Avatar from '@material-ui/core/Avatar'; +import Zoom from '@material-ui/core/Zoom'; import { useAlert } from "react-alert"; @@ -41,19 +45,30 @@ import ScheduleIcon from '@material-ui/icons/Schedule'; import CloudIcon from '@material-ui/icons/Cloud'; import BusinessIcon from '@material-ui/icons/Business'; -const Admin = (props) => { - const { globalUrl } = props; +const useStyles = makeStyles({ + notchedOutline: { + borderColor: "#f85a3e !important" + }, +}) +const Admin = (props) => { + const { globalUrl, userdata } = props; + + var upload = "" const theme = useTheme(); + const classes = useStyles(); const [firstRequest, setFirstRequest] = React.useState(true); const [modalUser, setModalUser] = React.useState({}); const [modalOpen, setModalOpen] = React.useState(false); + const [file, setFile] = React.useState(""); + const [fileBase64, setFileBase64] = React.useState(""); const [cloudSyncModalOpen, setCloudSyncModalOpen] = React.useState(false); const [cloudSyncApikey, setCloudSyncApikey] = React.useState(""); const [loading, setLoading] = React.useState(false); const [selectedOrganization, setSelectedOrganization] = React.useState({}); + const [organizationFeatures, setOrganizationFeatures] = React.useState({}); const [loginInfo, setLoginInfo] = React.useState(""); const [curTab, setCurTab] = React.useState(0); const [users, setUsers] = React.useState([]); @@ -244,6 +259,8 @@ const Admin = (props) => { selectedOrganization.cloud_sync = !selectedOrganization.cloud_sync setSelectedOrganization(selectedOrganization) setCloudSyncApikey("") + + handleGetOrg(userdata.active_org.id) } }) .catch(error => { @@ -312,6 +329,50 @@ const Admin = (props) => { }); } + const handleGetOrg = (orgId) => { + // Just use this one? + var baseurl = globalUrl + const url = baseurl + '/api/v1/orgs/'+orgId + fetch(url, { + method: 'GET', + credentials: "include", + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(response => + response.json().then(responseJson => { + if (responseJson["success"] === false) { + alert.error("Failed getting org: ", responseJson.readon) + } else { + setSelectedOrganization(responseJson) + var lists = { + "active": { + "triggers": [], + "features": [], + "sync": [], + }, + "inactive": { + "triggers": [], + "features": [], + "sync": [], + }, + } + + Object.keys(responseJson.sync_features).map(function(key, index) { + console.log(responseJson.sync_features[key]) + }) + + setOrganizationFeatures(lists) + } + }), + ) + .catch(error => { + console.log("Error getting org: ", error) + alert.error("Error getting current organization") + }); + } + const submitUser = (data) => { console.log("INPUT: ", data) @@ -609,6 +670,11 @@ const Admin = (props) => { getUsers() } + if (selectedOrganization.id === undefined && userdata !== undefined && userdata.active_org !== undefined) { + //setSelectedOrganization(userdata.active_org) + handleGetOrg(userdata.active_org.id) + } + const paperStyle = { maxWidth: 1250, margin: "auto", @@ -831,19 +897,21 @@ const Admin = (props) => { return ( - - - - - {primaryIcon} - - - - {secondaryIcon} - + + + + + + {primaryIcon} + + + + {secondaryIcon} + + ) } @@ -853,15 +921,21 @@ const Admin = (props) => { { "primary": "Workflows", "secondary": "", - "active": false, + "active": true, "icon": , }, { "primary": "Apps", "secondary": "", - "active": false, + "active": true, "icon": , }, + { + "primary": "Organization", + "secondary": "", + "active": true, + "icon": , + }, ] const cloudSyncModal = @@ -905,7 +979,7 @@ const Admin = (props) => { setCloudSyncApikey(event.target.value) }} /> - + + {orgSyncResponse.length > 0 ? + + Error from Shuffle: {orgSyncResponse} + + : null + } + {/* + + {syncList.map((data, index) => { + return ( + + ) + })} + + */} + Cloud sync features + + {Object.keys(selectedOrganization.sync_features).map(function(key, index) { + + // + const item = selectedOrganization.sync_features[key] + const griditem = { + "primary": key, + "secondary": "", + "active": item.active, + "icon": , + } + + return ( + + + + ) + })} + + + } + + : null + const modalView = { }} > - {curTab === 0 ? "Add user" : "Add environment"} + {curTab === 1 ? "Add user" : "Add environment"} - {curTab === 0 ? + {curTab === 1 ?
Username { onChange={(event) => changeModalData("Password", event.target.value)} />
- : curTab === 2 ? + : curTab === 3 ?
Environment Name { Cancel
- const usersView = curTab === 0 ? + const usersView = curTab === 1 ?

User management

@@ -1145,7 +1420,7 @@ const Admin = (props) => {
: null - const schedulesView = curTab === 3 ? + const schedulesView = curTab === 4 ?

Schedules

@@ -1194,7 +1469,7 @@ const Admin = (props) => {
: null - const appCategoryView = curTab === 6 ? + const appCategoryView = curTab === 7 ?

Categories

@@ -1270,7 +1545,7 @@ const Admin = (props) => {
: null - const authenticationView = curTab === 1 ? + const authenticationView = curTab === 2 ?

App Authentication

@@ -1355,7 +1630,7 @@ const Admin = (props) => {
: null - const environmentView = curTab === 2 ? + const environmentView = curTab === 3 ?

Environments

@@ -1456,7 +1731,7 @@ const Admin = (props) => {
: null - const organizationsTab = curTab === 5 ? + const organizationsTab = curTab === 6 ?

Organizations

@@ -1537,7 +1812,7 @@ const Admin = (props) => {
: null - const hybridTab = curTab === 4 ? + const hybridTab = curTab === 5 ?

Hybrid

@@ -1580,13 +1855,13 @@ const Admin = (props) => { // primary={environment.Registered ? "true" : "false"} const setConfig = (event, newValue) => { - if (newValue === 1) { + if (newValue === 2) { getAppAuthentication() - } else if (newValue === 2) { - getEnvironments() } else if (newValue === 3) { + getEnvironments() + } else if (newValue === 4) { getSchedules() - } else if (newValue === 5) { + } else if (newValue === 6) { getOrgs() } @@ -1608,6 +1883,7 @@ const Admin = (props) => { onChange={setConfig} aria-label="disabled tabs example" > + Organization/> Users /> App Authentication/> Environments/> @@ -1618,6 +1894,7 @@ const Admin = (props) => {
+ {organizationView} {authenticationView} {appCategoryView} {usersView} diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh index 84ffea4a..3d2f9ebe 100644 --- a/functions/onprem/worker/build.sh +++ b/functions/onprem/worker/build.sh @@ -1,5 +1,5 @@ NAME=worker -VERSION=0.6.0 +VERSION=0.8.0 echo "Running docker build with $NAME:$VERSION" CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .