mirror of
https://github.com/openziti/zrok.git
synced 2025-01-03 12:39:07 +01:00
let's get rid of the olde (#107)
This commit is contained in:
parent
a7349802c6
commit
c724699dd8
@ -1,137 +0,0 @@
|
|||||||
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.token }</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 = 100;
|
|
||||||
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,8 +1,8 @@
|
|||||||
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
||||||
import Register from "./Register";
|
import Register from "./register/Register";
|
||||||
import Console from "./console/Console";
|
import Console from "./console/Console";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import Login from "./Login";
|
import Login from "./login/Login";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [user, setUser] = useState();
|
const [user, setUser] = useState();
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import {useEffect, useState} from "react";
|
|
||||||
import Login from "./Login";
|
|
||||||
import Version from "./Version";
|
|
||||||
import Token from "./Token";
|
|
||||||
import Logout from "./Logout";
|
|
||||||
import Account from "./Account";
|
|
||||||
import {ReactFlowProvider} from "react-flow-renderer";
|
|
||||||
|
|
||||||
const Console = () => {
|
|
||||||
const [user, setUser] = useState();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const localUser = localStorage.getItem("user")
|
|
||||||
if (localUser) {
|
|
||||||
setUser(JSON.parse(localUser))
|
|
||||||
console.log('reloaded user', localUser)
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return (
|
|
||||||
<Login loginSuccess={setUser}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
setUser(null);
|
|
||||||
localStorage.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="zrok">
|
|
||||||
<div className="container">
|
|
||||||
<div className="header">
|
|
||||||
<img alt="ziggy goes to space" src="ziggy.svg" width="65px"/>
|
|
||||||
<p className="header-title">zrok</p>
|
|
||||||
<div className={"header-status"}>
|
|
||||||
<div>
|
|
||||||
<p>{user.email}</p>
|
|
||||||
<Version/>
|
|
||||||
</div>
|
|
||||||
<div className={"header-controls"}>
|
|
||||||
<Token user={user}/>
|
|
||||||
<Logout user={user} logout={logout}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="main">
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<Account user={user}/>
|
|
||||||
</ReactFlowProvider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Console;
|
|
@ -1,26 +0,0 @@
|
|||||||
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,75 +0,0 @@
|
|||||||
import DataTable from 'react-data-table-component';
|
|
||||||
import Services from './Services';
|
|
||||||
import Icon from "@mdi/react";
|
|
||||||
import {mdiCloseOutline} from "@mdi/js";
|
|
||||||
import * as environment from './api/environment';
|
|
||||||
|
|
||||||
const Environments = (props) => {
|
|
||||||
const humanizeDuration = require("humanize-duration")
|
|
||||||
const disableEnvironment = (envId) => {
|
|
||||||
if(window.confirm('really disable environment "' + envId +'"?')) {
|
|
||||||
environment.disable({body: {identity: envId}}).then(resp => {
|
|
||||||
console.log(resp);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'Description',
|
|
||||||
selector: row => row.environment.description,
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Host',
|
|
||||||
selector: row => row.environment.host,
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Address',
|
|
||||||
selector: row => row.environment.address,
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Identity',
|
|
||||||
selector: row => row.environment.zId,
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Actions',
|
|
||||||
selector: row => <>
|
|
||||||
<button data-value={row.environment.zId} onClick={e => disableEnvironment(row.environment.zId)} title={"Disable Environment '"+row.environment.zId+"'"}>
|
|
||||||
<Icon path={mdiCloseOutline} size={0.7}/>
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Uptime',
|
|
||||||
selector: row => humanizeDuration(Date.now() - row.environment.updatedAt),
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const servicesComponent = ({ data }) => <Services envId={data.environment.zId} services={data.services} />
|
|
||||||
const servicesExpanded = row => row.services != null && row.services.length > 0
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>Environments</h2>
|
|
||||||
{ props.overview && props.overview.length > 0 && (
|
|
||||||
<div>
|
|
||||||
<DataTable
|
|
||||||
columns={columns}
|
|
||||||
data={props.overview}
|
|
||||||
defaultSortFieldId={1}
|
|
||||||
expandableRows
|
|
||||||
expandableRowsComponent={servicesComponent}
|
|
||||||
expandableRowExpanded={servicesExpanded}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Environments;
|
|
@ -1,16 +0,0 @@
|
|||||||
import Icon from '@mdi/react';
|
|
||||||
import { mdiLogout } from '@mdi/js';
|
|
||||||
|
|
||||||
const logoutIcon = mdiLogout;
|
|
||||||
|
|
||||||
const Logout = (props) => {
|
|
||||||
const onClick = () => {
|
|
||||||
props.logout()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button onClick={onClick} aria-label={"log out"} title={"Log out"}><Icon path={logoutIcon} size={.7}/></button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Logout;
|
|
@ -1,21 +0,0 @@
|
|||||||
import Environments from './Environments';
|
|
||||||
import ReactFlow from "react-flow-renderer";
|
|
||||||
|
|
||||||
const Network = (props) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={"network"}>
|
|
||||||
<h1>Network</h1>
|
|
||||||
<ReactFlow
|
|
||||||
nodes={props.nodes}
|
|
||||||
edges={props.edges}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Environments
|
|
||||||
overview={props.overview}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Network;
|
|
@ -1,59 +0,0 @@
|
|||||||
import DataTable from 'react-data-table-component';
|
|
||||||
import {Sparklines, SparklinesLine, SparklinesSpots} from 'react-sparklines';
|
|
||||||
import {mdiCloseOutline} from "@mdi/js";
|
|
||||||
import Icon from "@mdi/react";
|
|
||||||
import * as service from './api/service';
|
|
||||||
|
|
||||||
const Services = (props) => {
|
|
||||||
const humanizeDuration = require("humanize-duration")
|
|
||||||
const unshareService = (envId, svcToken) => {
|
|
||||||
if(window.confirm('really disable service "' + svcToken +'"?')) {
|
|
||||||
service.unshare({body: {envZId: envId, svcToken: svcToken}}).then(resp => {
|
|
||||||
console.log(resp)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'Frontend',
|
|
||||||
selector: row => row.frontendEndpoint,
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Backend',
|
|
||||||
selector: row => row.backendProxyEndpoint,
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Actions',
|
|
||||||
selector: row => <>
|
|
||||||
<button data-value={row.name} onClick={e => unshareService(props.envId, row.token)} title={"Un-share Service '"+row.token+"'"}>
|
|
||||||
<Icon path={mdiCloseOutline} size={0.7}/>
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Uptime',
|
|
||||||
selector: row => humanizeDuration(Date.now() - row.updatedAt),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Activity',
|
|
||||||
cell: row => <Sparklines data={row.metrics} height={20} limit={60}><SparklinesLine color={"#3b2693"}/><SparklinesSpots/></Sparklines>
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"nested-services"}>
|
|
||||||
{ props.services && props.services.length > 0 && (
|
|
||||||
<DataTable
|
|
||||||
columns={columns}
|
|
||||||
data={props.services}
|
|
||||||
defaultSortFieldId={1}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Services;
|
|
@ -1,53 +0,0 @@
|
|||||||
import Icon from "@mdi/react";
|
|
||||||
import {mdiKey, mdiContentCopy} from "@mdi/js";
|
|
||||||
import Popover from "@mui/material/Popover";
|
|
||||||
import {useState} from "react";
|
|
||||||
|
|
||||||
const Token = (props) => {
|
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
|
||||||
|
|
||||||
const handlePopoverClick = (event) => {
|
|
||||||
setAnchorEl(event.currentTarget);
|
|
||||||
};
|
|
||||||
const handlePopoverClose = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
}
|
|
||||||
const popoverOpen = Boolean(anchorEl);
|
|
||||||
const popoverId = popoverOpen ? 'token-popover' : undefined;
|
|
||||||
|
|
||||||
const text = "zrok enable " + props.user.token
|
|
||||||
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>
|
|
||||||
<button aria-describedby={popoverId} onClick={handlePopoverClick} title={"See Enable Secret"}><Icon path={mdiKey} size={0.7}/></button>
|
|
||||||
<Popover
|
|
||||||
id={popoverId}
|
|
||||||
open={popoverOpen}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
onClose={handlePopoverClose}
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: 'bottom',
|
|
||||||
horizontal: 'left',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className={"popover"}>
|
|
||||||
<h3>Enable zrok access in your shell:</h3>
|
|
||||||
<pre>
|
|
||||||
$ <span id={"zrok-enable-command"}>{text}</span> <Icon path={mdiContentCopy} size={0.7} onClick={handleCopy}/>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Token;
|
|
@ -1,24 +0,0 @@
|
|||||||
import {useEffect, useState} from "react";
|
|
||||||
import * as metadata from "./api/metadata";
|
|
||||||
|
|
||||||
const Version = () => {
|
|
||||||
const [v, setV] = useState('');
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let mounted = true;
|
|
||||||
metadata.version().then(resp => {
|
|
||||||
if(mounted) {
|
|
||||||
setV(resp.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
mounted = false;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<p>{v}</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Version;
|
|
@ -1,7 +1,7 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {Button} from "react-bootstrap";
|
import {Button} from "react-bootstrap";
|
||||||
import Network from "./Network";
|
import Network from "./Network";
|
||||||
import {buildGraph, mergeGraph} from "./graph";
|
import {mergeGraph} from "./graph";
|
||||||
|
|
||||||
const Visualizer = (props) => {
|
const Visualizer = (props) => {
|
||||||
const [networkGraph, setNetworkGraph] = useState({nodes: [], links: []});
|
const [networkGraph, setNetworkGraph] = useState({nodes: [], links: []});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import * as account from './api/account';
|
import * as account from '../api/account';
|
||||||
|
|
||||||
const Login = (props) => {
|
const Login = (props) => {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
@ -34,7 +34,7 @@ const Login = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"fullscreen"}>
|
<div className={"fullscreen"}>
|
||||||
<img src={"ziggy.svg"} width={200}/>
|
<img src={"/ziggy.svg"} width={200}/>
|
||||||
<h1>zrok</h1>
|
<h1>zrok</h1>
|
||||||
{message}
|
{message}
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
@ -2,7 +2,7 @@ import Icon from "@mdi/react";
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {mdiContentCopy} from "@mdi/js";
|
import {mdiContentCopy} from "@mdi/js";
|
||||||
import * as account from "./api/account";
|
import * as account from "../api/account";
|
||||||
|
|
||||||
const RegistrationForm = (props) => {
|
const RegistrationForm = (props) => {
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
Loading…
Reference in New Issue
Block a user