From 9dd427f90c48356c48b267ae23ab28056acddecf Mon Sep 17 00:00:00 2001 From: Shahrad Elahi Date: Thu, 21 Dec 2023 17:54:10 +0330 Subject: [PATCH] feat: show total net usage and connection mode in server page --- .../copiable-text/CopiableText.svelte | 4 +- web/src/lib/components/ui/badge/index.ts | 1 + web/src/lib/wireguard/index.ts | 38 ++++++++++++---- web/src/routes/[serverId]/+page.server.ts | 29 +++++++++--- web/src/routes/[serverId]/+page.svelte | 45 ++++++++++++++++--- 5 files changed, 92 insertions(+), 25 deletions(-) diff --git a/web/src/lib/components/copiable-text/CopiableText.svelte b/web/src/lib/components/copiable-text/CopiableText.svelte index 6fa6ef2..d3b78e9 100644 --- a/web/src/lib/components/copiable-text/CopiableText.svelte +++ b/web/src/lib/components/copiable-text/CopiableText.svelte @@ -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() || ''); }; diff --git a/web/src/lib/components/ui/badge/index.ts b/web/src/lib/components/ui/badge/index.ts index 46e3f1f..c16fa00 100644 --- a/web/src/lib/components/ui/badge/index.ts +++ b/web/src/lib/components/ui/badge/index.ts @@ -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: { diff --git a/web/src/lib/wireguard/index.ts b/web/src/lib/wireguard/index.ts index 2c69fcf..09deb3b 100644 --- a/web/src/lib/wireguard/index.ts +++ b/web/src/lib/wireguard/index.ts @@ -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 { 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 { 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; +export type WgUsage = { + total: PeerUsage; + peers: Map; +}; export type PeerUsage = { rx: number; diff --git a/web/src/routes/[serverId]/+page.server.ts b/web/src/routes/[serverId]/+page.server.ts index 3b755d4..607376b 100644 --- a/web/src/routes/[serverId]/+page.server.ts +++ b/web/src/routes/[serverId]/+page.server.ts @@ -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'); } }, diff --git a/web/src/routes/[serverId]/+page.svelte b/web/src/routes/[serverId]/+page.svelte index c24375c..f28f348 100644 --- a/web/src/routes/[serverId]/+page.svelte +++ b/web/src/routes/[serverId]/+page.svelte @@ -1,8 +1,7 @@ @@ -87,15 +95,38 @@ {data.server.name} + + {#if data.server.tor} + + Tor + + {/if} +
 {data.server.address}/24 
+
 {data.server.listen} 
+ + +
+
+ + {prettyBytes(data.usage.total.tx)} +
+
+ + {prettyBytes(data.usage.total.rx)} +
+
+
+ +