refactor: Component-ify interface and peer
Signed-off-by: Christoph Heiss <contact@christoph-heiss.at>
This commit is contained in:
parent
00e43ad112
commit
f5b04e9c9f
65
src/components/interface.tsx
Normal file
65
src/components/interface.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import Peer from 'components/peer';
|
||||
import type { WireguardInterface } from 'lib/wireguard';
|
||||
import { formatTraffic } from 'lib/utils';
|
||||
|
||||
export default function Interface(props: WireguardInterface) {
|
||||
const {
|
||||
name,
|
||||
up,
|
||||
rxBytes,
|
||||
txBytes,
|
||||
listenPort,
|
||||
publicKey,
|
||||
peers,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="md:flex">
|
||||
<aside className="w-2/12 content">
|
||||
<h2 style={{ marginBottom: -5 }}>
|
||||
{name}
|
||||
</h2>
|
||||
{up
|
||||
? <div className="chip ~positive">Up</div>
|
||||
: <div className="chip ~critical">Down</div>}
|
||||
</aside>
|
||||
<div className="md:w-10/12 content">
|
||||
<div className="flex">
|
||||
<div className="grow">
|
||||
<h4>
|
||||
Listen port
|
||||
</h4>
|
||||
{listenPort}
|
||||
</div>
|
||||
|
||||
<div className="grow">
|
||||
<h4>
|
||||
RX traffic
|
||||
</h4>
|
||||
{formatTraffic(rxBytes)}
|
||||
</div>
|
||||
|
||||
<div className="grow">
|
||||
<h4>
|
||||
TX traffic
|
||||
</h4>
|
||||
{formatTraffic(txBytes)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>
|
||||
Public key
|
||||
</h4>
|
||||
<code>
|
||||
{publicKey}
|
||||
</code>
|
||||
</div>
|
||||
|
||||
{peers.map((peer) => (
|
||||
<Peer key={peer.publicKey} {...peer} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
101
src/components/peer.tsx
Normal file
101
src/components/peer.tsx
Normal file
|
@ -0,0 +1,101 @@
|
|||
import type { WireguardPeer } from 'lib/wireguard';
|
||||
import { formatKeepAlive, formatTimeDelta, formatTraffic } from 'lib/utils';
|
||||
|
||||
export default function Peer(props: WireguardPeer) {
|
||||
const {
|
||||
publicKey,
|
||||
hasPresharedKey,
|
||||
endpoint,
|
||||
keepAlive,
|
||||
lastHandshake,
|
||||
rxBytes,
|
||||
txBytes,
|
||||
allowedIps,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<span className="chip ~neutral">
|
||||
Peer
|
||||
</span>
|
||||
{!endpoint && (
|
||||
<span className="chip ~critical ml-2">
|
||||
Down
|
||||
</span>
|
||||
)}
|
||||
{hasPresharedKey
|
||||
? (
|
||||
<span className="chip ~positive ml-2">
|
||||
Has preshared key
|
||||
</span>
|
||||
) : (
|
||||
<span className="chip ~critical ml-2">
|
||||
No preshared key
|
||||
</span>
|
||||
)}
|
||||
<hr className="sep h-2" />
|
||||
|
||||
<div className="flex mb-4">
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
Endpoint
|
||||
</h4>
|
||||
{endpoint}
|
||||
</div>
|
||||
|
||||
<div className="basis-1/2">
|
||||
<h4>
|
||||
RX traffic
|
||||
</h4>
|
||||
{formatTraffic(rxBytes)}
|
||||
</div>
|
||||
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
TX traffic
|
||||
</h4>
|
||||
{formatTraffic(txBytes)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex mb-4">
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
Keep alive
|
||||
</h4>
|
||||
{formatKeepAlive(keepAlive)}
|
||||
</div>
|
||||
|
||||
<div className="basis-1/2">
|
||||
<h4>
|
||||
Last handshake
|
||||
</h4>
|
||||
{formatTimeDelta(lastHandshake)}
|
||||
{' '}
|
||||
ago
|
||||
</div>
|
||||
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
Allowed IPs
|
||||
</h4>
|
||||
{allowedIps.map((ip) => (
|
||||
<>
|
||||
<span>{ip}</span>
|
||||
<br />
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>
|
||||
Public key
|
||||
</h4>
|
||||
<code>
|
||||
{publicKey}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import { Temporal } from '@js-temporal/polyfill';
|
||||
|
||||
export function classNames(...args: any[]): string {
|
||||
const classes = [];
|
||||
|
||||
|
@ -57,3 +59,58 @@ export async function apiGet(url: string): Promise<any> {
|
|||
return Promise.resolve({});
|
||||
});
|
||||
}
|
||||
|
||||
export function formatTraffic(b: number): string {
|
||||
if (b < 1024 * 1024) {
|
||||
return `${(b / 1024).toFixed(2)} KiB`;
|
||||
}
|
||||
|
||||
if (b < 1024 * 1024 * 1024) {
|
||||
return `${(b / 1024 / 1024).toFixed(2)} MiB`;
|
||||
}
|
||||
|
||||
return `${(b / 1024 / 1024 / 1024).toFixed(2)} GiB`;
|
||||
}
|
||||
|
||||
export function formatTimeDelta(date: string): string {
|
||||
let result = '';
|
||||
|
||||
try {
|
||||
const from = Temporal.Instant.from(date).toZonedDateTimeISO(Temporal.Now.timeZone());
|
||||
const delta = Temporal.Now.zonedDateTimeISO().since(from, {
|
||||
largestUnit: 'day',
|
||||
smallestUnit: 'second',
|
||||
});
|
||||
|
||||
if (delta.days > 0) {
|
||||
result += `${delta.days}d `;
|
||||
}
|
||||
|
||||
if (delta.hours > 0) {
|
||||
result += `${delta.hours}h `;
|
||||
}
|
||||
|
||||
if (delta.minutes > 0) {
|
||||
result += `${delta.minutes}min `;
|
||||
}
|
||||
|
||||
if (delta.seconds > 0) {
|
||||
result += `${delta.seconds}s `;
|
||||
}
|
||||
|
||||
// return delta.toLocaleString('en-US', {
|
||||
// hours: 'narrow',
|
||||
// minutes: 'narrow',
|
||||
// seconds: 'narrow',
|
||||
// });
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function formatKeepAlive(keepAlive: number): string {
|
||||
return keepAlive !== 0 ? `${keepAlive}s` : 'disabled';
|
||||
}
|
||||
|
|
|
@ -1,64 +1,9 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import BaseLayout from 'components/base-layout';
|
||||
import Interface from 'components/interface';
|
||||
import { withSessionSsr } from 'lib/withSession';
|
||||
import { apiGet } from 'lib/utils';
|
||||
import type { WireguardInterface } from 'lib/wireguard';
|
||||
import { Temporal } from '@js-temporal/polyfill';
|
||||
|
||||
function formatTraffic(b: number): string {
|
||||
if (b < 1024 * 1024) {
|
||||
return `${(b / 1024).toFixed(2)} KiB`;
|
||||
}
|
||||
|
||||
if (b < 1024 * 1024 * 1024) {
|
||||
return `${(b / 1024 / 1024).toFixed(2)} MiB`;
|
||||
}
|
||||
|
||||
return `${(b / 1024 / 1024 / 1024).toFixed(2)} GiB`;
|
||||
}
|
||||
|
||||
function formatTimeDelta(date: string): string {
|
||||
let result = '';
|
||||
|
||||
try {
|
||||
const from = Temporal.Instant.from(date).toZonedDateTimeISO(Temporal.Now.timeZone());
|
||||
const delta = Temporal.Now.zonedDateTimeISO().since(from, {
|
||||
largestUnit: 'day',
|
||||
smallestUnit: 'second',
|
||||
});
|
||||
|
||||
if (delta.days > 0) {
|
||||
result += `${delta.days}d `;
|
||||
}
|
||||
|
||||
if (delta.hours > 0) {
|
||||
result += `${delta.hours}h `;
|
||||
}
|
||||
|
||||
if (delta.minutes > 0) {
|
||||
result += `${delta.minutes}min `;
|
||||
}
|
||||
|
||||
if (delta.seconds > 0) {
|
||||
result += `${delta.seconds}s `;
|
||||
}
|
||||
|
||||
// return delta.toLocaleString('en-US', {
|
||||
// hours: 'narrow',
|
||||
// minutes: 'narrow',
|
||||
// seconds: 'narrow',
|
||||
// });
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function formatKeepAlive(keepAlive: number): string {
|
||||
return keepAlive !== 0 ? `${keepAlive}s` : 'disabled';
|
||||
}
|
||||
|
||||
interface IndexProps {
|
||||
user: string;
|
||||
|
@ -71,7 +16,9 @@ export default function Index({ user }: IndexProps) {
|
|||
const fetchData = async () => {
|
||||
const res = await apiGet('/api/interfaces');
|
||||
|
||||
if (typeof res.error === 'string') {
|
||||
if (!res || typeof res.error === 'string') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(res);
|
||||
setFetchFailed(true);
|
||||
} else {
|
||||
setFetchFailed(false);
|
||||
|
@ -101,135 +48,7 @@ export default function Index({ user }: IndexProps) {
|
|||
</>
|
||||
)}
|
||||
{interfaces.map((wgif) => (
|
||||
<div className="md:flex" key={wgif.index}>
|
||||
<aside className="w-2/12 content">
|
||||
<h2 style={{ marginBottom: -5 }}>
|
||||
{wgif.name}
|
||||
</h2>
|
||||
{wgif.up
|
||||
? <div className="chip ~positive">Up</div>
|
||||
: <div className="chip ~critical">Down</div>}
|
||||
</aside>
|
||||
<div className="md:w-10/12 content">
|
||||
<div className="flex">
|
||||
<div className="grow">
|
||||
<h4>
|
||||
Listen port
|
||||
</h4>
|
||||
{wgif.listenPort}
|
||||
</div>
|
||||
|
||||
<div className="grow">
|
||||
<h4>
|
||||
RX traffic
|
||||
</h4>
|
||||
{formatTraffic(wgif.rxBytes)}
|
||||
</div>
|
||||
|
||||
<div className="grow">
|
||||
<h4>
|
||||
TX traffic
|
||||
</h4>
|
||||
{formatTraffic(wgif.txBytes)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>
|
||||
Public key
|
||||
</h4>
|
||||
<code>
|
||||
{wgif.publicKey}
|
||||
</code>
|
||||
</div>
|
||||
|
||||
{wgif.peers.map((peer) => (
|
||||
<div key={peer.publicKey} className="card">
|
||||
<span className="chip ~neutral">
|
||||
Peer
|
||||
</span>
|
||||
{!peer.endpoint && (
|
||||
<span className="chip ~critical ml-2">
|
||||
Down
|
||||
</span>
|
||||
)}
|
||||
{peer.hasPresharedKey
|
||||
? (
|
||||
<span className="chip ~positive ml-2">
|
||||
Has preshared key
|
||||
</span>
|
||||
) : (
|
||||
<span className="chip ~critical ml-2">
|
||||
No preshared key
|
||||
</span>
|
||||
)}
|
||||
<hr className="sep h-2" />
|
||||
|
||||
<div className="flex mb-4">
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
Endpoint
|
||||
</h4>
|
||||
{peer.endpoint}
|
||||
</div>
|
||||
|
||||
<div className="basis-1/2">
|
||||
<h4>
|
||||
RX traffic
|
||||
</h4>
|
||||
{formatTraffic(peer.rxBytes)}
|
||||
</div>
|
||||
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
TX traffic
|
||||
</h4>
|
||||
{formatTraffic(peer.txBytes)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex mb-4">
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
Keep alive
|
||||
</h4>
|
||||
{formatKeepAlive(peer.keepAlive)}
|
||||
</div>
|
||||
|
||||
<div className="basis-1/2">
|
||||
<h4>
|
||||
Last handshake
|
||||
</h4>
|
||||
{formatTimeDelta(peer.lastHandshake)}
|
||||
{' '}
|
||||
ago
|
||||
</div>
|
||||
|
||||
<div className="basis-1/4">
|
||||
<h4>
|
||||
Allowed IPs
|
||||
</h4>
|
||||
{peer.allowedIps.map((ip) => (
|
||||
<>
|
||||
<span>{ip}</span>
|
||||
<br />
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>
|
||||
Public key
|
||||
</h4>
|
||||
<code>
|
||||
{peer.publicKey}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Interface key={wgif.index} {...wgif} />
|
||||
))}
|
||||
</BaseLayout>
|
||||
);
|
||||
|
|
Reference in a new issue