mirror of
https://github.com/wireadmin/wireadmin
synced 2025-02-26 05:48:44 +00:00
feat: show total net usage and connection mode in server page
This commit is contained in:
parent
212363c7fa
commit
9dd427f90c
@ -3,12 +3,12 @@
|
||||
|
||||
export let showInHover: boolean = false;
|
||||
export let rootClass: string | undefined = undefined;
|
||||
export let value: string;
|
||||
export let value: string | number;
|
||||
let className: string | undefined = undefined;
|
||||
export { className as class };
|
||||
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(value);
|
||||
navigator.clipboard.writeText(value?.toString() || '');
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -11,6 +11,7 @@ export const badgeVariants = tv({
|
||||
destructive:
|
||||
'bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground',
|
||||
outline: 'text-foreground',
|
||||
tor: 'bg-purple-700 hover:bg-purple-700/80 border-transparent text-white',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
@ -11,6 +11,7 @@ import logger from '$lib/logger';
|
||||
import { sha256 } from '$lib/hash';
|
||||
import { fsAccess } from '$lib/fs-extra';
|
||||
import { getClient } from '$lib/redis';
|
||||
import { execaCommand } from 'execa';
|
||||
|
||||
export class WGServer {
|
||||
readonly id: string;
|
||||
@ -133,25 +134,41 @@ export class WGServer {
|
||||
|
||||
async hasInterface(): Promise<boolean> {
|
||||
const server = await this.get();
|
||||
return await Network.checkInterfaceExists(`wg${server.confId}`);
|
||||
try {
|
||||
const res = await execaCommand(`wg show wg${server.confId}`);
|
||||
return res.stdout.includes('wg');
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async getUsage(): Promise<WgUsage> {
|
||||
const server = await this.get();
|
||||
const hasInterface = await this.hasInterface();
|
||||
|
||||
const usages: WgUsage = {
|
||||
total: { rx: 0, tx: 0 },
|
||||
peers: new Map(),
|
||||
};
|
||||
|
||||
if (!hasInterface) {
|
||||
logger.error('GetUsage: interface does not exists');
|
||||
return new Map();
|
||||
logger.debug('GetUsage: interface does not exists');
|
||||
return usages;
|
||||
}
|
||||
|
||||
const res = await Shell.exec(`wg show wg${server.confId} transfer`);
|
||||
const lines = res.split('\n');
|
||||
const { stdout, stderr } = await execaCommand(`wg show wg${server.confId} transfer`);
|
||||
if (stderr) {
|
||||
logger.warn(`WgServer: GetUsage: ${stderr}`);
|
||||
return usages;
|
||||
}
|
||||
|
||||
const usages: WgUsage = new Map();
|
||||
const lines = stdout.split('\n');
|
||||
for (const line of lines) {
|
||||
const [peer, rx, tx] = line.split('\t');
|
||||
const [peer, tx, rx] = line.split('\t');
|
||||
if (!peer) continue;
|
||||
usages.set(peer, { rx: Number(rx), tx: Number(tx) });
|
||||
usages.peers.set(peer, { rx: Number(rx), tx: Number(tx) });
|
||||
usages.total.rx += Number(rx);
|
||||
usages.total.tx += Number(tx);
|
||||
}
|
||||
|
||||
return usages;
|
||||
@ -180,7 +197,10 @@ export class WGServer {
|
||||
}
|
||||
}
|
||||
|
||||
export type WgUsage = Map<string, PeerUsage>;
|
||||
export type WgUsage = {
|
||||
total: PeerUsage;
|
||||
peers: Map<string, PeerUsage>;
|
||||
};
|
||||
|
||||
export type PeerUsage = {
|
||||
rx: number;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { type Actions, error } from '@sveltejs/kit';
|
||||
import { type Actions, error, redirect } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { findServer, generateWgKey, WGServer } from '$lib/wireguard';
|
||||
import { NameSchema } from '$lib/wireguard/schema';
|
||||
@ -8,10 +8,25 @@ import logger from '$lib/logger';
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => {
|
||||
const { serverId } = params;
|
||||
const server = await findServer(serverId);
|
||||
const exists = await WGServer.exists(serverId ?? '');
|
||||
|
||||
if (server) {
|
||||
return { server };
|
||||
if (exists) {
|
||||
const wg = new WGServer(serverId);
|
||||
const server = await wg.get();
|
||||
|
||||
if (server.status === 'up') {
|
||||
const hasInterface = await wg.hasInterface();
|
||||
if (!hasInterface) {
|
||||
await wg.start();
|
||||
}
|
||||
}
|
||||
|
||||
const usage = await wg.getUsage();
|
||||
|
||||
return {
|
||||
server,
|
||||
usage,
|
||||
};
|
||||
}
|
||||
|
||||
throw error(404, 'Not found');
|
||||
@ -95,8 +110,8 @@ export const actions: Actions = {
|
||||
|
||||
const server = await findServer(serverId ?? '');
|
||||
if (!server) {
|
||||
console.error('Server not found');
|
||||
throw error(404, 'Not found');
|
||||
logger.error('Action: ChangeState: Server not found');
|
||||
throw redirect(303, '/');
|
||||
}
|
||||
|
||||
const form = await request.formData();
|
||||
@ -128,7 +143,7 @@ export const actions: Actions = {
|
||||
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
console.error('Exception:', e);
|
||||
logger.error('Exception: ChangeState:', e);
|
||||
throw error(500, 'Unhandled Exception');
|
||||
}
|
||||
},
|
||||
|
@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import { Card } from '$lib/components/ui/card';
|
||||
import CreatePeerDialog from './CreatePeerDialog.svelte';
|
||||
import { CardContent, CardFooter, CardHeader, CardTitle } from '$lib/components/ui/card/index.js';
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import Peer from './Peer.svelte';
|
||||
import fetchAction from '$lib/fetch-action';
|
||||
@ -12,6 +11,8 @@
|
||||
import { MiddleEllipsis } from '$lib/components/middle-ellipsis';
|
||||
import { goto, invalidateAll } from '$app/navigation';
|
||||
import { Empty } from '$lib/components/empty';
|
||||
import prettyBytes from 'pretty-bytes';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@ -57,16 +58,13 @@
|
||||
const resp = await fetchAction({
|
||||
action: '?/change-server-state',
|
||||
method: 'POST',
|
||||
form: {
|
||||
state,
|
||||
},
|
||||
form: { state },
|
||||
});
|
||||
if (resp.statusText !== 'OK') {
|
||||
console.error('err: failed to change server state.');
|
||||
console.error('error: failed to change server state.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('server state changed!');
|
||||
if (state === 'remove') {
|
||||
await goto('/');
|
||||
return;
|
||||
@ -74,6 +72,16 @@
|
||||
|
||||
await invalidateAll();
|
||||
};
|
||||
|
||||
// revalidate every 2 seconds
|
||||
const interval = setInterval(() => {
|
||||
invalidateAll();
|
||||
}, 2000);
|
||||
|
||||
onDestroy(() => {
|
||||
clearInterval(interval);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
@ -87,15 +95,38 @@
|
||||
{data.server.name}
|
||||
</CopiableText>
|
||||
</DetailRow>
|
||||
|
||||
{#if data.server.tor}
|
||||
<DetailRow label={'Mode'}>
|
||||
<Badge variant="tor">Tor</Badge>
|
||||
</DetailRow>
|
||||
{/if}
|
||||
|
||||
<DetailRow label={'IP address'}>
|
||||
<pre> {data.server.address}/24 </pre>
|
||||
</DetailRow>
|
||||
|
||||
<DetailRow label={'Listen Port'}>
|
||||
<pre> {data.server.listen} </pre>
|
||||
</DetailRow>
|
||||
|
||||
<DetailRow label={'Total Usage'}>
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<div class="flex items-center gap-x-1.5">
|
||||
<i class="fas fa-arrow-up text-gray-500"></i>
|
||||
<span>{prettyBytes(data.usage.total.tx)}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-1.5">
|
||||
<i class="fas fa-arrow-down text-gray-500"></i>
|
||||
<span>{prettyBytes(data.usage.total.rx)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DetailRow>
|
||||
|
||||
<DetailRow label={'Status'}>
|
||||
<Badge variant={data.server.status === 'up' ? 'success' : 'destructive'} />
|
||||
</DetailRow>
|
||||
|
||||
<DetailRow label={'Public Key'}>
|
||||
<CopiableText value={data.server.publicKey}>
|
||||
<MiddleEllipsis content={data.server.publicKey} maxLength={12} />
|
||||
|
Loading…
Reference in New Issue
Block a user