mirror of
https://github.com/openziti/zrok.git
synced 2024-11-26 10:04:16 +01:00
better component hierarchy; display enable help when no environments (#84)
This commit is contained in:
parent
5a2d5fcbb1
commit
7d1c69ad0e
137
ui/src/Account.js
Normal file
137
ui/src/Account.js
Normal file
@ -0,0 +1,137 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {useReactFlow} from "react-flow-renderer";
|
||||
import Icon from "@mdi/react";
|
||||
import {mdiAccessPointNetwork, mdiDesktopClassic} from "@mdi/js";
|
||||
import dagre from "dagre";
|
||||
import * as metadata from "./api/metadata";
|
||||
import Network from "./Network";
|
||||
import Enable from "./Enable";
|
||||
|
||||
const Account = (props) => {
|
||||
const [mode, setMode] = useState(<></>);
|
||||
const reactFlow = useReactFlow();
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
metadata.overview().then(resp => {
|
||||
if(mounted) {
|
||||
let overview = resp.data
|
||||
let g = buildGraph(resp.data)
|
||||
let nodes = getLayout(g)
|
||||
let edges = g.edges
|
||||
|
||||
if(resp.data.length > 0) {
|
||||
setMode(<Network
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
overview={overview}
|
||||
/>)
|
||||
reactFlow.fitView({maxZoom: 1})
|
||||
} else {
|
||||
setMode(<Enable token={props.user.token}/>)
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
let interval = setInterval(() => {
|
||||
metadata.overview().then(resp => {
|
||||
if(mounted) {
|
||||
let overview = resp.data
|
||||
let g = buildGraph(resp.data)
|
||||
let nodes = getLayout(g)
|
||||
let edges = g.edges
|
||||
|
||||
if(resp.data.length > 0) {
|
||||
setMode(<Network
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
overview={overview}
|
||||
/>)
|
||||
reactFlow.fitView({maxZoom: 1})
|
||||
} else {
|
||||
setMode(<Enable token={props.user.token}/>)
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
return () => {
|
||||
mounted = false
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <>{mode}</>
|
||||
}
|
||||
|
||||
function buildGraph(overview) {
|
||||
let out = {
|
||||
nodes: [],
|
||||
edges: []
|
||||
}
|
||||
let id = 1
|
||||
overview.forEach((item) => {
|
||||
let envId = id
|
||||
out.nodes.push({
|
||||
id: '' + envId,
|
||||
data: { label: <div><Icon path={mdiDesktopClassic} size={0.75} className={"flowNode"}/> { item.environment.description } </div> },
|
||||
position: { x: (id * 25), y: 0 },
|
||||
style: { width: 'fit-content', backgroundColor: '#aaa', color: 'white' },
|
||||
type: 'input',
|
||||
draggable: true
|
||||
});
|
||||
id++
|
||||
if(item.services != null) {
|
||||
item.services.forEach((item) => {
|
||||
out.nodes.push({
|
||||
id: '' + id,
|
||||
data: {label: <div><Icon path={mdiAccessPointNetwork} size={0.75} className={"flowNode"}/> { item.frontend }</div>},
|
||||
position: {x: (id * 25), y: 0},
|
||||
style: { width: 'fit-content', backgroundColor: '#9367ef', color: 'white' },
|
||||
type: 'output',
|
||||
draggable: true
|
||||
})
|
||||
out.edges.push({
|
||||
id: 'e' + envId + '-' + id,
|
||||
source: '' + envId,
|
||||
target: '' + id,
|
||||
animated: true
|
||||
})
|
||||
id++
|
||||
});
|
||||
}
|
||||
});
|
||||
return out
|
||||
}
|
||||
|
||||
const nodeWidth = 215;
|
||||
const nodeHeight = 75;
|
||||
|
||||
function getLayout(overview) {
|
||||
const dagreGraph = new dagre.graphlib.Graph();
|
||||
dagreGraph.setGraph({ rankdir: 'TB' });
|
||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||
|
||||
overview.nodes.forEach((n) => {
|
||||
dagreGraph.setNode(n.id, { width: nodeWidth, height: nodeHeight });
|
||||
})
|
||||
overview.edges.forEach((e) => {
|
||||
dagreGraph.setEdge(e.source, e.target);
|
||||
})
|
||||
dagre.layout(dagreGraph);
|
||||
|
||||
return overview.nodes.map((n) => {
|
||||
const nodeWithPosition = dagreGraph.node(n.id);
|
||||
n.targetPosition = 'top';
|
||||
n.sourcePosition = 'bottom';
|
||||
n.position = {
|
||||
x: nodeWithPosition.x - (nodeWidth / 2) + (Math.random() / 1000) + 50,
|
||||
y: nodeWithPosition.y - (nodeHeight / 2) + 50,
|
||||
}
|
||||
return n;
|
||||
});
|
||||
}
|
||||
|
||||
export default Account
|
@ -1,17 +1,16 @@
|
||||
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
||||
import Register from "./Register";
|
||||
import Overview from "./Overview";
|
||||
import Console from "./Console";
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path={"/"} element={<Overview />}/>
|
||||
<Route path={"/"} element={<Console/>}/>
|
||||
<Route path={"register/:token"} element={<Register/>} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
export default App
|
@ -3,10 +3,10 @@ import Login from "./Login";
|
||||
import Version from "./Version";
|
||||
import Token from "./Token";
|
||||
import Logout from "./Logout";
|
||||
import Network from "./Network";
|
||||
import Account from "./Account";
|
||||
import {ReactFlowProvider} from "react-flow-renderer";
|
||||
|
||||
const Overview = () => {
|
||||
const Console = () => {
|
||||
const [user, setUser] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
@ -47,7 +47,7 @@ const Overview = () => {
|
||||
</div>
|
||||
<div className="main">
|
||||
<ReactFlowProvider>
|
||||
<Network />
|
||||
<Account user={user}/>
|
||||
</ReactFlowProvider>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,4 +55,4 @@ const Overview = () => {
|
||||
);
|
||||
}
|
||||
|
||||
export default Overview;
|
||||
export default Console;
|
26
ui/src/Enable.js
Normal file
26
ui/src/Enable.js
Normal file
@ -0,0 +1,26 @@
|
||||
import Icon from "@mdi/react";
|
||||
import {mdiContentCopy} from "@mdi/js";
|
||||
|
||||
const Enable = (props) => {
|
||||
const handleCopy = async () => {
|
||||
let copiedText = document.getElementById("zrok-enable-command").innerHTML;
|
||||
try {
|
||||
await navigator.clipboard.writeText(copiedText);
|
||||
console.log("copied enable command");
|
||||
} catch(err) {
|
||||
console.error("failed to copy", err);
|
||||
}
|
||||
}
|
||||
|
||||
return <>
|
||||
<div id={"zrok-enable"}>
|
||||
<h1>Enable an Environment</h1>
|
||||
<p>To enable your shell for zrok, use this command:</p>
|
||||
<pre>
|
||||
$ <span id={"zrok-enable-command"}>zrok enable {props.token}</span> <Icon path={mdiContentCopy} size={0.7} onClick={handleCopy}/>
|
||||
</pre>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export default Enable
|
@ -1,134 +1,21 @@
|
||||
import Environments from './Environments';
|
||||
import * as metadata from './api/metadata';
|
||||
import {useEffect, useLayoutEffect, useRef, useState} from "react";
|
||||
import ReactFlow, {isNode, useNodesState, useReactFlow} from "react-flow-renderer";
|
||||
import dagre from 'dagre';
|
||||
import { mdiDesktopClassic, mdiAccessPointNetwork } from '@mdi/js';
|
||||
import Icon from "@mdi/react";
|
||||
|
||||
const Network = () => {
|
||||
const [overview, setOverview] = useState([]);
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||
const [edges, setEdges] = useState([]);
|
||||
const reactFlow = useReactFlow();
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
metadata.overview().then(resp => {
|
||||
if(mounted) {
|
||||
setOverview(resp.data)
|
||||
|
||||
let g = buildGraph(resp.data)
|
||||
setNodes(getLayout(g))
|
||||
setEdges(g.edges)
|
||||
reactFlow.fitView({maxZoom: 1})
|
||||
}
|
||||
});
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
let interval = setInterval(() => {
|
||||
metadata.overview().then(resp => {
|
||||
if(mounted) {
|
||||
setOverview(resp.data)
|
||||
|
||||
let g = buildGraph(resp.data)
|
||||
setNodes(getLayout(g))
|
||||
setEdges(g.edges)
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
return () => {
|
||||
mounted = false
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, [])
|
||||
import ReactFlow from "react-flow-renderer";
|
||||
|
||||
const Network = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<div className={"network"}>
|
||||
<h1>Network</h1>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
nodes={props.nodes}
|
||||
edges={props.edges}
|
||||
/>
|
||||
</div>
|
||||
<Environments
|
||||
overview={overview}
|
||||
overview={props.overview}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function buildGraph(overview) {
|
||||
let out = {
|
||||
nodes: [],
|
||||
edges: []
|
||||
}
|
||||
let id = 1
|
||||
overview.forEach((item) => {
|
||||
let envId = id
|
||||
out.nodes.push({
|
||||
id: '' + envId,
|
||||
data: { label: <div><Icon path={mdiDesktopClassic} size={0.75} className={"flowNode"}/> { item.environment.description } </div> },
|
||||
position: { x: (id * 25), y: 0 },
|
||||
style: { width: 'fit-content', backgroundColor: '#aaa', color: 'white' },
|
||||
type: 'input',
|
||||
draggable: true
|
||||
});
|
||||
id++
|
||||
if(item.services != null) {
|
||||
item.services.forEach((item) => {
|
||||
out.nodes.push({
|
||||
id: '' + id,
|
||||
data: {label: <div><Icon path={mdiAccessPointNetwork} size={0.75} className={"flowNode"}/> { item.frontend }</div>},
|
||||
position: {x: (id * 25), y: 0},
|
||||
style: { width: 'fit-content', backgroundColor: '#9367ef', color: 'white' },
|
||||
type: 'output',
|
||||
draggable: true
|
||||
})
|
||||
out.edges.push({
|
||||
id: 'e' + envId + '-' + id,
|
||||
source: '' + envId,
|
||||
target: '' + id,
|
||||
animated: true
|
||||
})
|
||||
id++
|
||||
});
|
||||
}
|
||||
});
|
||||
return out
|
||||
}
|
||||
|
||||
const nodeWidth = 215;
|
||||
const nodeHeight = 75;
|
||||
|
||||
function getLayout(overview) {
|
||||
const dagreGraph = new dagre.graphlib.Graph();
|
||||
dagreGraph.setGraph({ rankdir: 'TB' });
|
||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||
|
||||
overview.nodes.forEach((n) => {
|
||||
dagreGraph.setNode(n.id, { width: nodeWidth, height: nodeHeight });
|
||||
})
|
||||
overview.edges.forEach((e) => {
|
||||
dagreGraph.setEdge(e.source, e.target);
|
||||
})
|
||||
dagre.layout(dagreGraph);
|
||||
|
||||
return overview.nodes.map((n) => {
|
||||
const nodeWithPosition = dagreGraph.node(n.id);
|
||||
n.targetPosition = 'top';
|
||||
n.sourcePosition = 'bottom';
|
||||
n.position = {
|
||||
x: nodeWithPosition.x - (nodeWidth / 2) + (Math.random() / 1000) + 50,
|
||||
y: nodeWithPosition.y - (nodeHeight / 2) + 50,
|
||||
}
|
||||
return n;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export default Network;
|
@ -14,6 +14,14 @@ h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Russo One', sans-serif;
|
||||
}
|
||||
|
||||
#zrok-enable {
|
||||
text-align: center;
|
||||
margin-top: 100px;
|
||||
}
|
||||
#zrok-enable h1 {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
margin-left: 10%;
|
||||
|
Loading…
Reference in New Issue
Block a user