mirror of
https://github.com/openziti/zrok.git
synced 2024-12-22 14:50:55 +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 Register from "./Register";
|
||||
import Register from "./register/Register";
|
||||
import Console from "./console/Console";
|
||||
import {useEffect, useState} from "react";
|
||||
import Login from "./Login";
|
||||
import Login from "./login/Login";
|
||||
|
||||
const App = () => {
|
||||
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 {Button} from "react-bootstrap";
|
||||
import Network from "./Network";
|
||||
import {buildGraph, mergeGraph} from "./graph";
|
||||
import {mergeGraph} from "./graph";
|
||||
|
||||
const Visualizer = (props) => {
|
||||
const [networkGraph, setNetworkGraph] = useState({nodes: [], links: []});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {useState} from "react";
|
||||
import * as account from './api/account';
|
||||
import * as account from '../api/account';
|
||||
|
||||
const Login = (props) => {
|
||||
const [email, setEmail] = useState('');
|
||||
@ -34,7 +34,7 @@ const Login = (props) => {
|
||||
|
||||
return (
|
||||
<div className={"fullscreen"}>
|
||||
<img src={"ziggy.svg"} width={200}/>
|
||||
<img src={"/ziggy.svg"} width={200}/>
|
||||
<h1>zrok</h1>
|
||||
{message}
|
||||
<form onSubmit={handleSubmit}>
|
@ -2,7 +2,7 @@ import Icon from "@mdi/react";
|
||||
import { useParams } from 'react-router-dom';
|
||||
import {useEffect, useState} from "react";
|
||||
import {mdiContentCopy} from "@mdi/js";
|
||||
import * as account from "./api/account";
|
||||
import * as account from "../api/account";
|
||||
|
||||
const RegistrationForm = (props) => {
|
||||
const [password, setPassword] = useState('');
|
Loading…
Reference in New Issue
Block a user