Added org and suborg swapping POC

This commit is contained in:
frikky
2021-07-30 01:36:41 +02:00
parent 67453eb9f5
commit eceb3a2fca
8 changed files with 223 additions and 126 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ require (
github.com/docker/go-units v0.4.0 // indirect
github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect
github.com/frikky/kin-openapi v0.39.0
github.com/frikky/shuffle-shared v0.0.72
github.com/frikky/shuffle-shared v0.0.74
github.com/fsouza/go-dockerclient v1.7.2
github.com/ghodss/yaml v1.0.0
github.com/go-git/go-billy/v5 v5.0.0
+8 -4
View File
@@ -739,12 +739,15 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) {
}
}
apikey := ""
if count != 0 {
if user.Role != "admin" {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Can't register without being admin (2)"}`))
return
}
} else {
apikey = uuid.NewV4().String()
}
// Gets a struct of Username, password
@@ -819,7 +822,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) {
}
}
err = createNewUser(data.Username, data.Password, role, "", currentOrg)
err = createNewUser(data.Username, data.Password, role, apikey, currentOrg)
if err != nil {
log.Printf("[WARNING] Failed registering user: %s", err)
resp.WriteHeader(401)
@@ -828,7 +831,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) {
}
resp.WriteHeader(200)
resp.Write([]byte(`{"success": true}`))
resp.Write([]byte(fmt.Sprintf(`{"success": true, "apikey": "%s"}`, apikey)))
log.Printf("[INFO] %s Successfully registered.", data.Username)
}
@@ -4147,12 +4150,12 @@ func runInitEs(ctx context.Context) {
r, err := git.Clone(storer, fs, cloneOptions)
if err != nil {
log.Printf("Failed loading repo into memory (init): %s", err)
log.Printf("[WARNING] Failed loading repo into memory (init): %s", err)
}
dir, err := fs.ReadDir("")
if err != nil {
log.Printf("Failed reading folder: %s", err)
log.Printf("[WARNING] Failed reading folder (init): %s", err)
}
_ = r
//iterateAppGithubFolders(fs, dir, "", "testing")
@@ -5837,6 +5840,7 @@ func initHandlers() {
r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleGetOrg).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleEditOrg).Methods("POST", "OPTIONS")
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
// 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.
+2 -2
View File
@@ -1,7 +1,7 @@
version: '3'
services:
frontend:
#build: ./frontend
build: ./frontend
image: ghcr.io/frikky/shuffle-frontend:nightly
container_name: shuffle-frontend
hostname: shuffle-frontend
@@ -16,7 +16,7 @@ services:
depends_on:
- backend
backend:
#build: ./backend
build: ./backend
image: ghcr.io/frikky/shuffle-backend:nightly
container_name: shuffle-backend
hostname: ${BACKEND_HOSTNAME}
+1 -1
View File
@@ -105,7 +105,7 @@ const App = (message, props) => {
</div> :
<div style={{ backgroundColor: "#1F2023", color: "rgba(255, 255, 255, 0.65)", minHeight: "100vh" }}>
<ScrollToTop setCurpath={setCurpath} />
<Header cookies={cookies} removeCookie={removeCookie} isLoaded={isLoaded} globalUrl={globalUrl} setIsLoggedIn={setIsLoggedIn} isLoggedIn={isLoggedIn} userdata={userdata} {...props} />
<Header checkLogin={checkLogin} cookies={cookies} removeCookie={removeCookie} isLoaded={isLoaded} globalUrl={globalUrl} setIsLoggedIn={setIsLoggedIn} isLoggedIn={isLoggedIn} userdata={userdata} {...props} />
<Route exact path="/login" render={props => <LoginPage isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
<Route exact path="/admin" render={props => <Admin userdata={userdata} isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
<Route exact path="/admin/:key" render={props => <Admin isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+48 -23
View File
@@ -390,10 +390,10 @@ const Admin = (props) => {
if (responseJson.reason !== undefined) {
alert.error(responseJson.reason)
} else {
alert.error("Failed setting new password")
alert.error("Failed creating suborg")
}
} else {
alert.success("Successfully updated password!")
alert.success("Successfully created suborg!")
setSelectedUserModalOpen(false)
}
@@ -499,6 +499,10 @@ const Admin = (props) => {
if (responseJson["success"] === false) {
alert.error("Failed getting org: ", responseJson.readon)
} else {
if (responseJson.sync_features === undefined || responseJson.sync_features === null) {
responseJson.sync_features = {}
}
setSelectedOrganization(responseJson)
var lists = {
"active": {
@@ -1727,7 +1731,7 @@ const Admin = (props) => {
}
<Typography style={{marginTop: 40, marginLeft: 10, marginBottom: 5,}}>Cloud sync features</Typography>
<Grid container style={{width: "100%", marginBottom: 15, }}>
{Object.keys(selectedOrganization.sync_features).map(function(key, index) {
{selectedOrganization.sync_features === undefined || selectedOrganization.sync_features === null ? null : Object.keys(selectedOrganization.sync_features).map(function(key, index) {
if (key === "schedule") {
return null
}
@@ -1813,7 +1817,7 @@ const Admin = (props) => {
}}
>
<DialogTitle><span style={{ color: "white" }}>
{curTab === 1 ? "Add user" : curTab === 7 ? "Add Sub-Organization" : "Add environment"}
{curTab === 1 ? "Add user" : curTab === 6 ? "Add Sub-Organization" : "Add environment"}
</span></DialogTitle>
<DialogContent>
{curTab === 1 && isCloud ?
@@ -1821,7 +1825,7 @@ const Admin = (props) => {
We'll send an email to invite them to your organization.
</Typography>
:
curTab === 7 ?
curTab === 6 ?
<Typography variant="body1" style={{marginBottom: 10}}>
The organization created will become a child of your current organization, and be available to you.
</Typography>
@@ -1876,7 +1880,7 @@ const Admin = (props) => {
</span>
}
</div>
: curTab === 7 ?
: curTab === 6 ?
<div>
Name
<TextField
@@ -1938,7 +1942,7 @@ const Admin = (props) => {
} else {
submitUser(modalUser)
}
} else if (curTab === 7) {
} else if (curTab === 6) {
createSubOrg(selectedOrganization.id, orgName)
} else if (curTab === 5) {
submitEnvironment(modalUser)
@@ -2513,6 +2517,7 @@ const Admin = (props) => {
bgColor = "#1f2023"
}
return (
<ListItem key={index} style={{backgroundColor: bgColor}}>
<ListItemText
@@ -2696,7 +2701,7 @@ const Admin = (props) => {
</div>
: null
const organizationsTab = curTab === 7 ?
const organizationsTab = curTab === 6 ?
<div>
<div style={{marginTop: 20, marginBottom: 20,}}>
<h2 style={{display: "inline",}}>Organizations</h2>
@@ -2716,17 +2721,21 @@ const Admin = (props) => {
<List>
<ListItem>
<ListItemText
primary="Name"
style={{minWidth: 150, maxWidth: 150}}
primary="Logo"
style={{minWidth: 100, maxWidth: 100}}
/>
<ListItemText
primary="id"
style={{minWidth: 200, maxWidth: 200}}
primary="Name"
style={{minWidth: 250, maxWidth: 250}}
/>
<ListItemText
primary="Your role"
style={{minWidth: 150, maxWidth: 150}}
/>
<ListItemText
primary="id"
style={{minWidth: 400, maxWidth: 400}}
/>
<ListItemText
primary="Selected"
style={{minWidth: 150, maxWidth: 150}}
@@ -2736,25 +2745,41 @@ const Admin = (props) => {
style={{minWidth: 150, maxWidth: 150}}
/>
</ListItem>
{organizations !== undefined && organizations !== null && organizations.length > 0 ?
{userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
<span>
{organizations.map((data, index) => {
{userdata.orgs.map((data, index) => {
const isSelected = props.userdata.active_org.id === undefined ? "False" : props.userdata.active_org.id === data.id ? "True" : "False"
const imagesize = 40
const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", }
const image = data.image === "" ?
<img alt={data.name} src={theme.palette.defaultImage} style={imageStyle} />
:
<img alt={data.name} src={data.image} style={imageStyle} />
var bgColor = "#27292d"
if (index % 2 === 0) {
bgColor = "#1f2023"
}
return (
<ListItem key={index}>
<ListItem key={index} style={{backgroundColor: bgColor,}}>
<ListItemText
primary={data.name}
style={{minWidth: 150, maxWidth: 150}}
primary={image}
style={{minWidth: 100, maxWidth: 100}}
/>
<ListItemText
primary={data.id}
style={{minWidth: 200, maxWidth: 200}}
primary={data.name}
style={{minWidth: 250, maxWidth: 250}}
/>
<ListItemText
primary={data.role}
style={{minWidth: 150, maxWidth: 150}}
/>
<ListItemText
primary={data.id}
style={{minWidth: 400, maxWidth: 400}}
/>
<ListItemText
primary={isSelected}
style={{minWidth: 150, maxWidth: 150}}
@@ -2776,7 +2801,7 @@ const Admin = (props) => {
</div>
: null
const hybridTab = curTab === 6 ?
const hybridTab = curTab === 7 ?
<div>
<div style={{marginTop: 20, marginBottom: 20,}}>
<h2 style={{display: "inline",}}>Hybrid</h2>
@@ -2836,9 +2861,9 @@ const Admin = (props) => {
<Tab label=<span><DescriptionIcon style={iconStyle} />Files</span> />
<Tab label=<span><ScheduleIcon style={iconStyle} />Schedules</span> />
{isCloud ? null : <Tab label=<span><EcoIcon style={iconStyle} />Environments</span>/>}
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><CloudIcon style={iconStyle} /> Hybrid</span>/> : null}
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><BusinessIcon style={iconStyle} /> Organizations</span>/> : null}
{window.location.protocol === "http:" && window.location.port === "3000" ? <Tab label=<span><LockIcon style={iconStyle} />Categories</span>/> : null}
{isCloud ? null : <Tab label=<span><BusinessIcon style={iconStyle} /> Organizations</span>/>}
{/*window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><CloudIcon style={iconStyle} /> Hybrid</span>/> : null*/}
{/*window.location.protocol === "http:" && window.location.port === "3000" ? <Tab label=<span><LockIcon style={iconStyle} />Categories</span>/> : null*/}
</Tabs>
<Divider style={{marginTop: 0, marginBottom: 10, backgroundColor: "rgb(91, 96, 100)"}} />
<div style={{padding: 15}}>
+29 -1
View File
@@ -1,6 +1,7 @@
import React, { useEffect} from 'react';
import { useInterval } from 'react-powerhooks';
import { makeStyles } from '@material-ui/core/styles';
import { useTheme } from '@material-ui/core/styles';
import {Avatar, Grid, Paper, Tooltip, Divider, Button, TextField, FormControl, IconButton, Menu, MenuItem, FormControlLabel, Chip, Switch, Typography, Zoom, CircularProgress, Dialog, DialogTitle, DialogActions, DialogContent} from '@material-ui/core';
import {Close as CloseIcon, Compare as CompareIcon, Maximize as MaximizeIcon, Minimize as MinimizeIcon, AddCircle as AddCircleIcon, Toc as TocIcon, Send as SendIcon, Search as SearchIcon, FileCopy as FileCopyIcon, Delete as DeleteIcon, BubbleChart as BubbleChartIcon, Restore as RestoreIcon, Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudUpload as CloudUploadIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons';
@@ -328,6 +329,7 @@ export const validateJson = (showResult) => {
const Workflows = (props) => {
const { globalUrl, isLoggedIn, isLoaded, removeCookie, cookies, userdata} = props;
document.title = "Shuffle - Workflows"
const theme = useTheme();
const referenceUrl = globalUrl+"/api/v1/hooks/"
const alert = useAlert()
@@ -391,6 +393,10 @@ const Workflows = (props) => {
return true
} else if (curWorkflow.tags.includes(filter)) {
return true
} else if (curWorkflow.owner === filter) {
return true
} else if (curWorkflow.org_id === filter) {
return true
} else if (curWorkflow.actions !== null && curWorkflow.actions !== undefined) {
const newfilter = filter.toLowerCase()
for (var key in curWorkflow.actions) {
@@ -1065,16 +1071,38 @@ const Workflows = (props) => {
parsedName = parsedName.slice(0,26)+".."
}
const actions = data.actions !== null ? data.actions.length : 0
const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data)
const imagesize = 22
const foundOrg = userdata.orgs.find(org => org.id === data["org_id"])
//position: "absolute", bottom: 5, right: -5,
const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", marginRight: 10, marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0, borderRadius: 10, border: foundOrg.id === userdata.active_org.id ? `4px solid ${boxColor}` : null, cursor: "pointer", marginRight: 10, }
//<Tooltip title={`Org: ${foundOrg.name}`} placement="bottom">
const image = foundOrg.image === "" ?
<img alt={foundOrg.name} src={theme.palette.defaultImage} style={imageStyle} />
:
<img alt={foundOrg.name} src={foundOrg.image} style={imageStyle} onClick={() => {
//setFilteredWorkflows(newWorkflows)
}}/>
return (
<Grid item xs={4} style={{padding: "12px 10px 12px 10px",}}>
<Paper square style={paperAppStyle} >
<div style={{position: "absolute", bottom: 1, left: 1, height: 12, width: 12, backgroundColor: boxColor, borderRadius: "0 100px 0 0",}} />
<Grid item style={{display: "flex", flexDirection: "column", width: "100%"}}>
<Grid item style={{display: "flex", maxHeight: 34,}}>
<Tooltip title={`Org "${foundOrg.name}"`} placement="bottom">
<div styl={{cursor: "pointer"}} onClick={() => {
addFilter(foundOrg.id)
//setFilters(["Org "+foundOrg.name])
//setFilteredWorkflows(newWorkflows)
}}>
{image}
</div>
</Tooltip>
<Tooltip title={`Edit ${data.name}`} placement="bottom">
<Typography variant="body1" style={{marginBottom: 0, paddingBottom: 0, maxHeight: 30, flex: 10,}}>
<Link to={"/workflows/"+data.id} style={{textDecoration: "none", color: "inherit",}}>