From 90ee3debf06db79ef0add959349ad7898da18143 Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Thu, 17 Sep 2026 07:22:20 +0100 Subject: [PATCH] Initialize client forms --- db/schema.ts | 4 + db/types/db.ts | 3 +- src/components/general/admin-hero.tsx | 14 ---- .../general/setup-main-host-button.tsx | 16 +++- src/data/app-data.ts | 1 + src/dict/variables-dict.ts | 5 ++ .../backend/setup/grab-host-dir-names.ts | 69 +++++++++++++++++ .../backend/setup/setup-wireguard-host.ts | 77 +++++++------------ src/functions/check-if-main-host-is-set.ts | 3 - .../admin/(partials)/header-page-title.tsx | 29 ------- src/layouts/admin/(sections)/Header.tsx | 21 +++-- src/pages/__root.server.ts | 4 + .../hosts/(functions)/derive-host-config.ts | 42 +++++++--- .../(sections)/interface-config-section.tsx | 67 +++++++++------- .../clients/(partials)/clients-list.tsx | 48 ++++++++++++ .../(sections)/clients-list-section.tsx | 18 +++++ .../client-form/add-client-form-title.tsx | 16 ++++ .../client-form/add-client-form.tsx | 31 ++++++++ .../(sections)/add-client-form-section.tsx | 16 ++++ .../clients/add-client/index.server.ts | 21 +++++ .../[host_id]/clients/add-client/index.tsx | 29 +++++++ .../hosts/[host_id]/clients/index.server.ts | 43 +++++++++++ .../admin/hosts/[host_id]/clients/index.tsx | 43 +++++++++++ .../admin/hosts/[host_id]/index.server.ts | 43 +++++++++++ src/pages/admin/hosts/[host_id]/index.tsx | 30 ++++++++ src/pages/admin/hosts/index.tsx | 21 ++++- src/types/index.ts | 9 +++ src/types/sql-joins.ts | 6 ++ 28 files changed, 584 insertions(+), 145 deletions(-) create mode 100644 src/functions/backend/setup/grab-host-dir-names.ts delete mode 100644 src/layouts/admin/(partials)/header-page-title.tsx create mode 100644 src/pages/admin/hosts/[host_id]/clients/(partials)/clients-list.tsx create mode 100644 src/pages/admin/hosts/[host_id]/clients/(sections)/clients-list-section.tsx create mode 100644 src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form-title.tsx create mode 100644 src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form.tsx create mode 100644 src/pages/admin/hosts/[host_id]/clients/add-client/(sections)/add-client-form-section.tsx create mode 100644 src/pages/admin/hosts/[host_id]/clients/add-client/index.server.ts create mode 100644 src/pages/admin/hosts/[host_id]/clients/add-client/index.tsx create mode 100644 src/pages/admin/hosts/[host_id]/clients/index.server.ts create mode 100644 src/pages/admin/hosts/[host_id]/clients/index.tsx create mode 100644 src/pages/admin/hosts/[host_id]/index.server.ts create mode 100644 src/pages/admin/hosts/[host_id]/index.tsx diff --git a/db/schema.ts b/db/schema.ts index 6366290..decb0d6 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -195,6 +195,10 @@ const schema: BUN_SQLITE_DatabaseSchemaType = { fieldName: "user_id", dataType: "INTEGER", }, + { + fieldName: "public_ip_address", + dataType: "TEXT", + }, { fieldName: "wg_ip_address", dataType: "TEXT", diff --git a/db/types/db.ts b/db/types/db.ts index 29f865c..38150ef 100644 --- a/db/types/db.ts +++ b/db/types/db.ts @@ -130,6 +130,7 @@ export type BUN_SQLITE_WGUI_HOSTS = { */ updated_at?: number | ""; user_id?: number | ""; + public_ip_address?: string; wg_ip_address?: string; public_key?: string; } @@ -165,7 +166,7 @@ export type BUN_SQLITE_WGUI_VARIABLES = { * The time when the record was updated. (Unix Timestamp) */ updated_at?: number | ""; - key?: "main_host_wg_ip_address" | "main_host_wg_public_key" | "main_host_wg_private_key" | ""; + key?: "main_host_wg_ip_address" | "main_host_public_ip_address" | "main_host_wg_public_key" | "main_host_wg_private_key" | ""; value?: string; } diff --git a/src/components/general/admin-hero.tsx b/src/components/general/admin-hero.tsx index e78a4f9..a99cbb3 100644 --- a/src/components/general/admin-hero.tsx +++ b/src/components/general/admin-hero.tsx @@ -15,8 +15,6 @@ type Props = { }; export default function AdminHero({ title, description, buttons }: Props) { - const { pageProps } = useContext(AppContext); - return (
@@ -30,18 +28,6 @@ export default function AdminHero({ title, description, buttons }: Props) { description ) ) : null} - {buttons} diff --git a/src/components/general/setup-main-host-button.tsx b/src/components/general/setup-main-host-button.tsx index 54b227a..72b4e64 100755 --- a/src/components/general/setup-main-host-button.tsx +++ b/src/components/general/setup-main-host-button.tsx @@ -16,6 +16,7 @@ import { import Span from "../twui/layout/Span"; import Tag from "../twui/elements/Tag"; import { AppData } from "@/src/data/app-data"; +import type { APIResponseObject } from "@moduletrace/bunext/types"; type Props = { button_props?: Omit, "title">; @@ -205,11 +206,18 @@ export default function SetupMainHostButton({ button_props }: Props) { setLoading(true); setStatus(undefined); - fetchApi(`/api/admin/setup-main-host`, { - method: "POST", - body: { ip_address: wgIP }, - }) + fetchApi( + `/api/admin/setup-main-host`, + { + method: "POST", + body: { ip_address: wgIP }, + }, + ) .then((res) => { + if (res.success) { + window.location.reload(); + } + setStatus({ success: res.success, error: !res.success, diff --git a/src/data/app-data.ts b/src/data/app-data.ts index 6431e28..ad87bc2 100644 --- a/src/data/app-data.ts +++ b/src/data/app-data.ts @@ -19,4 +19,5 @@ export const AppData = { WireguardHostID: 0, DefaultPrivateIP: `10.1.0.1`, + DefaultWGListenPort: 51820, } as const; diff --git a/src/dict/variables-dict.ts b/src/dict/variables-dict.ts index d2e7e26..de2cd5b 100644 --- a/src/dict/variables-dict.ts +++ b/src/dict/variables-dict.ts @@ -4,6 +4,11 @@ export const Variables = [ value: "main_host_wg_ip_address", description: `Private IP address to use for the main Wireguard host. Eg. 10.1.0.1`, }, + { + title: `Main Host Public IP Address`, + value: "main_host_public_ip_address", + description: `Private IP address to use for the main Public host. Eg. 10.1.0.1`, + }, { title: `Main Host Wireguard Public Key`, value: "main_host_wg_public_key", diff --git a/src/functions/backend/setup/grab-host-dir-names.ts b/src/functions/backend/setup/grab-host-dir-names.ts new file mode 100644 index 0000000..b456cf1 --- /dev/null +++ b/src/functions/backend/setup/grab-host-dir-names.ts @@ -0,0 +1,69 @@ +import { AppData } from "@/src/data/app-data"; +import deriveWireguardInterfaceName from "@/src/utils/derive-wireguard-interface-name"; +import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db"; +import path from "path"; +import grabDirNames from "@/src/utils/grab-dir-names"; + +type Params = { + host?: BUN_SQLITE_WGUI_HOSTS; +}; + +const { + WGUI_LIB_HOSTS_CONFIGS_DIR, + WGUI_LIB_HOST_CLIENTS_DIR_NAME, + WGUI_LIB_HOST_IPTABLES_DIR_NAME, + WIREGUARD_PUBLIC_KEY_FILE_NAME, + WIREGUARD_PRIVATE_KEY_FILE_NAME, +} = grabDirNames(); + +export default function grabHostDirnames({ host }: Params) { + const HOST_ID = host?.id || AppData["WireguardHostID"]; + + const INTERFACE_NAME = deriveWireguardInterfaceName({ host_id: HOST_ID }); + + const HOST_CONFIG_DIR = path.join( + WGUI_LIB_HOSTS_CONFIGS_DIR, + String(HOST_ID), + ); + + const HOST_CLIENTS_DIR = path.join( + HOST_CONFIG_DIR, + WGUI_LIB_HOST_CLIENTS_DIR_NAME, + ); + + const HOST_IPTABLES_DIR = path.join( + HOST_CONFIG_DIR, + WGUI_LIB_HOST_IPTABLES_DIR_NAME, + ); + + const HOST_CONFIG_FILE = path.join( + HOST_CONFIG_DIR, + `${INTERFACE_NAME}.conf`, + ); + + const HOST_PUBLIC_KEY_FILE = path.join( + HOST_CONFIG_DIR, + WIREGUARD_PUBLIC_KEY_FILE_NAME, + ); + + const HOST_PRIVATE_KEY_FILE = path.join( + HOST_CONFIG_DIR, + WIREGUARD_PRIVATE_KEY_FILE_NAME, + ); + + const POST_UP_PATH = path.join(HOST_IPTABLES_DIR, `up.sh`); + const POST_DOWN_PATH = path.join(HOST_IPTABLES_DIR, `down.sh`); + + return { + HOST_ID, + HOST_CONFIG_DIR, + HOST_CLIENTS_DIR, + HOST_IPTABLES_DIR, + HOST_CONFIG_FILE, + HOST_PUBLIC_KEY_FILE, + HOST_PRIVATE_KEY_FILE, + INTERFACE_NAME, + POST_UP_PATH, + POST_DOWN_PATH, + }; +} diff --git a/src/functions/backend/setup/setup-wireguard-host.ts b/src/functions/backend/setup/setup-wireguard-host.ts index 1392f5d..fc8d1e4 100644 --- a/src/functions/backend/setup/setup-wireguard-host.ts +++ b/src/functions/backend/setup/setup-wireguard-host.ts @@ -3,25 +3,18 @@ import type { BUN_SQLITE_WGUI_HOSTS, BUN_SQLITE_WGUI_VARIABLES, } from "@/db/types/db"; -import { AppData } from "@/src/data/app-data"; import grabDirNames from "@/src/utils/grab-dir-names"; -import deriveWireguardInterfaceName from "@/src/utils/derive-wireguard-interface-name"; import { execSync } from "node:child_process"; -import path from "node:path"; import grabHostNetworkInterface from "./grab-host-network-interface"; import type { APIResponseObject } from "@moduletrace/bunext/types"; import BunSQLite from "@moduletrace/bun-sqlite"; import type { TableType, User } from "@/src/types"; import checkPrivateIPAvailability from "./check-private-ip-availability"; import manageWireguardHost from "./manage-wireguard-host"; +import grabHostDirnames from "./grab-host-dir-names"; -const { - WGUI_LIB_HOSTS_CONFIGS_DIR, - WIREGUARD_PRIVATE_KEY_FILE_NAME, - WIREGUARD_PUBLIC_KEY_FILE_NAME, - WGUI_LIB_HOST_CLIENTS_DIR_NAME, - WGUI_LIB_HOST_IPTABLES_DIR_NAME, -} = grabDirNames(); +const { WIREGUARD_PRIVATE_KEY_FILE_NAME, WIREGUARD_PUBLIC_KEY_FILE_NAME } = + grabDirNames(); type Params = { host?: BUN_SQLITE_WGUI_HOSTS; @@ -34,29 +27,18 @@ export default async function setupWireguardHost({ wg_subnet_ip, user, }: Params): Promise { - const host_id = host?.id || AppData["WireguardHostID"]; - - const INTERFACE_NAME = deriveWireguardInterfaceName({ host_id }); - - const HOST_CONFIG_DIR = path.join( - WGUI_LIB_HOSTS_CONFIGS_DIR, - String(host_id), - ); - - const HOST_CLIENTS_DIR = path.join( - WGUI_LIB_HOSTS_CONFIGS_DIR, - WGUI_LIB_HOST_CLIENTS_DIR_NAME, - ); - - const HOST_IPTABLES_DIR = path.join( - WGUI_LIB_HOSTS_CONFIGS_DIR, - WGUI_LIB_HOST_IPTABLES_DIR_NAME, - ); - - const HOST_CONFIG_FILE = path.join( + const { + HOST_CLIENTS_DIR, HOST_CONFIG_DIR, - `${INTERFACE_NAME}.conf`, - ); + HOST_CONFIG_FILE, + HOST_ID, + HOST_IPTABLES_DIR, + HOST_PRIVATE_KEY_FILE, + HOST_PUBLIC_KEY_FILE, + INTERFACE_NAME, + POST_DOWN_PATH, + POST_UP_PATH, + } = grabHostDirnames({ host }); const variables_res = await BunSQLite.select< BUN_SQLITE_WGUI_VARIABLES, @@ -75,7 +57,7 @@ export default async function setupWireguardHost({ query: { query: { host_id: { - value: host_id, + value: HOST_ID, }, }, }, @@ -111,6 +93,8 @@ export default async function setupWireguardHost({ pre_sh += `set -e\n`; pre_sh += `mkdir -p ${HOST_CONFIG_DIR}\n`; + pre_sh += `mkdir -p ${HOST_CLIENTS_DIR}\n`; + pre_sh += `mkdir -p ${HOST_IPTABLES_DIR}\n`; pre_sh += `cd ${HOST_CONFIG_DIR}\n`; pre_sh += `if [ ! -f ${WIREGUARD_PRIVATE_KEY_FILE_NAME} ]; then\n`; pre_sh += ` wg genkey | tee ${WIREGUARD_PRIVATE_KEY_FILE_NAME} | wg pubkey > ${WIREGUARD_PUBLIC_KEY_FILE_NAME}\n`; @@ -126,17 +110,15 @@ export default async function setupWireguardHost({ } try { - const HOST_PUBLIC_KEY = execSync( - `cat ${path.join(HOST_CONFIG_DIR, WIREGUARD_PUBLIC_KEY_FILE_NAME)}`, - { encoding: "utf-8" }, - ).trim(); + const HOST_PUBLIC_KEY = execSync(`cat ${HOST_PUBLIC_KEY_FILE}`, { + encoding: "utf-8", + }).trim(); - const HOST_PRIVATE_KEY = execSync( - `cat ${path.join(HOST_CONFIG_DIR, WIREGUARD_PRIVATE_KEY_FILE_NAME)}`, - { encoding: "utf-8" }, - ).trim(); + const HOST_PRIVATE_KEY = execSync(`cat ${HOST_PRIVATE_KEY_FILE}`, { + encoding: "utf-8", + }).trim(); - if (host_id == 0) { + if (HOST_ID == 0) { const update_variables = await BunSQLite.insert< BUN_SQLITE_WGUI_VARIABLES, TableType @@ -155,12 +137,10 @@ export default async function setupWireguardHost({ update_on_duplicate: true, }); - console.log("update_variables", update_variables); - if (!update_variables.success) { throw new Error(`Couldn't update host variables`); } - } else if (host_id) { + } else if (HOST_ID) { const update_host = await BunSQLite.insert< BUN_SQLITE_WGUI_HOSTS, TableType @@ -168,7 +148,7 @@ export default async function setupWireguardHost({ table: "hosts", data: [ { - id: host_id, + id: HOST_ID, user_id: user.id, wg_ip_address: HOST_WG_IP, public_key: HOST_PUBLIC_KEY, @@ -182,9 +162,6 @@ export default async function setupWireguardHost({ sh += `set -e\n`; - const POST_UP_PATH = path.join(HOST_IPTABLES_DIR, `up.sh`); - const POST_DOWN_PATH = path.join(HOST_IPTABLES_DIR, `down.sh`); - sh += `cd ${HOST_CONFIG_DIR}\n`; sh += `cat > ${POST_UP_PATH} << EOF\n`; @@ -247,7 +224,7 @@ export default async function setupWireguardHost({ const exec = execSync(sh, { encoding: "utf-8" }); const manage_res = manageWireguardHost({ - host_id, + host_id: HOST_ID, action: "restart", }); diff --git a/src/functions/check-if-main-host-is-set.ts b/src/functions/check-if-main-host-is-set.ts index 1097c24..f9cf68e 100644 --- a/src/functions/check-if-main-host-is-set.ts +++ b/src/functions/check-if-main-host-is-set.ts @@ -9,9 +9,6 @@ export default function checkIfMainHostIsSet({ variables }: Params) { Boolean( variables?.find((v) => v.key == "main_host_wg_ip_address")?.value, ) && - Boolean( - variables?.find((v) => v.key == "main_host_wg_private_key")?.value, - ) && Boolean( variables?.find((v) => v.key == "main_host_wg_public_key")?.value, ); diff --git a/src/layouts/admin/(partials)/header-page-title.tsx b/src/layouts/admin/(partials)/header-page-title.tsx deleted file mode 100644 index 7deaa01..0000000 --- a/src/layouts/admin/(partials)/header-page-title.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { AppContext } from "@/src/pages/__root"; -import { ChevronRight } from "lucide-react"; -import { useContext } from "react"; - -const SECTION_TITLES: Record = { - "/admin": "Dashboard", - "/admin/clients": "Clients", - "/admin/host": "Host", - "/admin/settings": "Settings", -}; - -export default function HeaderPageTitle() { - const { pageProps } = useContext(AppContext); - const pathname = pageProps?.url?.pathname || "/admin"; - const title = SECTION_TITLES[pathname] || "Admin"; - - return ( - - - Admin - - - {title} - - ); -} diff --git a/src/layouts/admin/(sections)/Header.tsx b/src/layouts/admin/(sections)/Header.tsx index ebe78bb..67cc6ff 100755 --- a/src/layouts/admin/(sections)/Header.tsx +++ b/src/layouts/admin/(sections)/Header.tsx @@ -2,15 +2,16 @@ import Row from "@/src/components/twui/layout/Row"; import HeaderUser from "../../main/(partials)/header-user"; import LogoIcon from "@/src/components/general/logo-icon"; import MobileNav from "../(partials)/MobileNav"; -import HeaderPageTitle from "../(partials)/header-page-title"; import { twMerge } from "tailwind-merge"; +import Breadcrumbs from "@/src/components/twui/elements/Breadcrumbs"; +import { useContext } from "react"; +import { AppContext } from "@/src/pages/__root"; type Props = {}; export default function Header({}: Props) { - /** - * Render component - */ + const { pageProps } = useContext(AppContext); + return (
- + diff --git a/src/pages/__root.server.ts b/src/pages/__root.server.ts index 6249256..856b3c8 100644 --- a/src/pages/__root.server.ts +++ b/src/pages/__root.server.ts @@ -1,6 +1,7 @@ import type { BunextPageServerFn } from "@moduletrace/bunext/types"; import type { PagePropsType } from "../types"; import userAuth from "../functions/backend/auth/user-auth"; +import grabHostDirnames from "../functions/backend/setup/grab-host-dir-names"; const server: BunextPageServerFn = async ({ req, url }) => { const { user, user_types } = await userAuth({ req }); @@ -27,6 +28,8 @@ const server: BunextPageServerFn = async ({ req, url }) => { }; } + const main_host_dir_names = grabHostDirnames({}); + return { props: { environment: process.env.NODE_ENV, @@ -36,6 +39,7 @@ const server: BunextPageServerFn = async ({ req, url }) => { }, user, user_types, + main_host_dir_names, }, }; }; diff --git a/src/pages/admin/hosts/(functions)/derive-host-config.ts b/src/pages/admin/hosts/(functions)/derive-host-config.ts index 4fc5a8b..9428eb4 100644 --- a/src/pages/admin/hosts/(functions)/derive-host-config.ts +++ b/src/pages/admin/hosts/(functions)/derive-host-config.ts @@ -2,16 +2,12 @@ import { AppData } from "@/src/data/app-data"; import deriveWireguardInterfaceName from "@/src/utils/derive-wireguard-interface-name"; import type { BUN_SQLITE_WGUI_CLIENTS, - BUN_SQLITE_WGUI_HOSTS, BUN_SQLITE_WGUI_VARIABLES, } from "@/db/types/db"; - -const LISTEN_PORT = 51820; -const IP_TABLES_DIR = `/var/lib/wgui/iptables`; -const HOSTS_CONFIG_DIR = `/var/lib/wgui/hosts`; +import type { BUN_SQLITE_WGUI_HOSTS_JOIN } from "@/src/types/sql-joins"; type Params = { - host?: BUN_SQLITE_WGUI_HOSTS; + host?: BUN_SQLITE_WGUI_HOSTS_JOIN; variables?: BUN_SQLITE_WGUI_VARIABLES[]; clients?: BUN_SQLITE_WGUI_CLIENTS[]; }; @@ -23,18 +19,42 @@ export default function deriveHostConfig({ host, variables, clients }: Params) { host?.wg_ip_address || variables?.find((v) => v.key == "main_host_wg_ip_address")?.value; + const public_key = + host?.public_key || + variables?.find((v) => v.key == "main_host_wg_public_key")?.value; + const interface_name = deriveWireguardInterfaceName({ host_id }); + // const HOST_CONFIG_DIR = path.join( + // WGUI_LIB_HOSTS_CONFIGS_DIR, + // String(host_id), + // ); + + // const HOST_CLIENTS_DIR = path.join( + // HOST_CONFIG_DIR, + // WGUI_LIB_HOST_CLIENTS_DIR_NAME, + // ); + + // const HOST_IPTABLES_DIR = path.join( + // HOST_CONFIG_DIR, + // WGUI_LIB_HOST_IPTABLES_DIR_NAME, + // ); + + // const HOST_CONFIG_FILE = path.join( + // HOST_CONFIG_DIR, + // `${interface_name}.conf`, + // ); + return { host_id, interface_name, - config_path: `${HOSTS_CONFIG_DIR}/${interface_name}.conf`, + config_path: host?.dir_names?.HOST_CONFIG_FILE, address: wg_ip_address ? `${wg_ip_address}/24` : undefined, - listen_port: LISTEN_PORT, - post_up: `${IP_TABLES_DIR}/${host_id}-up.sh`, - post_down: `${IP_TABLES_DIR}/${host_id}-down.sh`, - public_key: host?.public_key, + listen_port: AppData["DefaultWGListenPort"], + post_up: host?.dir_names?.POST_UP_PATH, + post_down: host?.dir_names?.POST_DOWN_PATH, + public_key: public_key, client_count: clients?.filter((client) => (client.host_id || 0) == host_id) .length || 0, diff --git a/src/pages/admin/hosts/(sections)/interface-config-section.tsx b/src/pages/admin/hosts/(sections)/interface-config-section.tsx index 2c26f84..67ee696 100644 --- a/src/pages/admin/hosts/(sections)/interface-config-section.tsx +++ b/src/pages/admin/hosts/(sections)/interface-config-section.tsx @@ -13,9 +13,14 @@ import type { BUN_SQLITE_WGUI_HOSTS, BUN_SQLITE_WGUI_VARIABLES, } from "@/db/types/db"; +import Divider from "@/src/components/twui/layout/Divider"; +import H3 from "@/src/components/twui/layout/H3"; +import type { BUN_SQLITE_WGUI_HOSTS_JOIN } from "@/src/types/sql-joins"; +import { useContext } from "react"; +import { AppContext } from "@/src/pages/__root"; type Props = { - host?: BUN_SQLITE_WGUI_HOSTS; + host?: BUN_SQLITE_WGUI_HOSTS_JOIN; variables?: BUN_SQLITE_WGUI_VARIABLES[]; clients?: BUN_SQLITE_WGUI_CLIENTS[]; }; @@ -25,12 +30,23 @@ export default function InterfaceConfigSection({ variables, clients, }: Props) { - const config = deriveHostConfig({ host, variables, clients }); + const { pageProps } = useContext(AppContext); + + const final_host: BUN_SQLITE_WGUI_HOSTS_JOIN = host + ? host + : { + dir_names: pageProps?.main_host_dir_names || undefined, + }; + + const config = deriveHostConfig({ host: final_host, variables, clients }); const rows = [ { keyName: "Address", value: config.address || "—" }, { keyName: "ListenPort", value: String(config.listen_port) }, - , + { + keyName: "PublicKey", + value: config.public_key ? config.public_key : "Not set", + }, { keyName: "PostUp", value: config.post_up }, { keyName: "PostDown", value: config.post_down }, ]; @@ -51,33 +67,32 @@ export default function InterfaceConfigSection({ return ( - - -

- Interface configuration -

-

+ + +

Interface configuration

+

+ {config.config_path} +

+
+ + navigator.clipboard?.writeText(buildConfigText()) + } > - {config.config_path} -

- - - navigator.clipboard?.writeText(buildConfigText()) - } - > - Copy config - -
-
+ Copy config + + + [Interface] {rows.map((row) => { - if (!row) return; + if (!row?.value) return; return ( -
+
); } diff --git a/src/pages/admin/hosts/[host_id]/clients/(partials)/clients-list.tsx b/src/pages/admin/hosts/[host_id]/clients/(partials)/clients-list.tsx new file mode 100644 index 0000000..ad4cf99 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/(partials)/clients-list.tsx @@ -0,0 +1,48 @@ +import Loading from "@/src/components/twui/elements/Loading"; +import Paper from "@/src/components/twui/elements/Paper"; +import Center from "@/src/components/twui/layout/Center"; +import Span from "@/src/components/twui/layout/Span"; +import Stack from "@/src/components/twui/layout/Stack"; +import { useAdminCrudGet } from "@/src/hooks/use-admin-crud-get"; +import { AppContext } from "@/src/pages/__root"; +import { useContext } from "react"; + +type Props = { + host_id: string | number; +}; + +export default function ClientsList({ host_id }: Props) { + const { pageProps } = useContext(AppContext); + + const { res: clients } = useAdminCrudGet({ + table: "clients", + sql_query: { + query: { + host_id: { value: host_id }, + }, + }, + initial_res: pageProps?.clients || undefined, + }); + + if (!clients) { + return ( + +
+ +
+
+ ); + } + + if (!clients[0]) { + return ( + +
+ No Clients added yet +
+
+ ); + } + + return {}; +} diff --git a/src/pages/admin/hosts/[host_id]/clients/(sections)/clients-list-section.tsx b/src/pages/admin/hosts/[host_id]/clients/(sections)/clients-list-section.tsx new file mode 100644 index 0000000..9824c96 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/(sections)/clients-list-section.tsx @@ -0,0 +1,18 @@ +import H2 from "@/src/components/twui/layout/H2"; +import Section from "@/src/components/twui/layout/Section"; +import { AppContext } from "@/src/pages/__root"; +import { useContext } from "react"; +import ClientsList from "../(partials)/clients-list"; + +export default function ClientsListSection() { + const { pageProps } = useContext(AppContext); + + const clients = pageProps?.clients || []; + + return ( +
+

{clients.length} Clients

+ +
+ ); +} diff --git a/src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form-title.tsx b/src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form-title.tsx new file mode 100644 index 0000000..5550460 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form-title.tsx @@ -0,0 +1,16 @@ +import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db"; +import Input from "@/src/components/twui/form/Input"; +import useFormInit from "@/src/hooks/use-form-init"; + +export default function AddClientFormTitle({ + form, + setForm, +}: ReturnType>) { + return ( + + ); +} diff --git a/src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form.tsx b/src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form.tsx new file mode 100644 index 0000000..e15ed5a --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/add-client/(partials)/client-form/add-client-form.tsx @@ -0,0 +1,31 @@ +import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db"; +import Form from "@/src/components/twui/form/Form"; +import Stack from "@/src/components/twui/layout/Stack"; +import useFormInit from "@/src/hooks/use-form-init"; +import AddClientFormTitle from "./add-client-form-title"; + +type Props = { + existing_client?: BUN_SQLITE_WGUI_CLIENTS; + existing_client_full?: BUN_SQLITE_WGUI_CLIENTS; + host_id?: string | number; +}; + +export default function AddClientForm({ + existing_client, + existing_client_full, + host_id, +}: Props) { + const init = useFormInit({ + existing: existing_client, + existing_full: existing_client_full, + title: "Add Client", + }); + + return ( +
+ + + +
+ ); +} diff --git a/src/pages/admin/hosts/[host_id]/clients/add-client/(sections)/add-client-form-section.tsx b/src/pages/admin/hosts/[host_id]/clients/add-client/(sections)/add-client-form-section.tsx new file mode 100644 index 0000000..3cbc9ab --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/add-client/(sections)/add-client-form-section.tsx @@ -0,0 +1,16 @@ +import Section from "@/src/components/twui/layout/Section"; +import AddClientForm from "../(partials)/client-form/add-client-form"; +import { useContext } from "react"; +import { AppContext } from "@/src/pages/__root"; + +export default function AddClientFormSection() { + const { pageProps, query } = useContext(AppContext); + + const host_id = query?.host_id || 0; + + return ( +
+ +
+ ); +} diff --git a/src/pages/admin/hosts/[host_id]/clients/add-client/index.server.ts b/src/pages/admin/hosts/[host_id]/clients/add-client/index.server.ts new file mode 100644 index 0000000..9965d85 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/add-client/index.server.ts @@ -0,0 +1,21 @@ +import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db"; +import type { PagePropsType, PageQueryObject, TableType } from "@/src/types"; +import BunSQLite from "@moduletrace/bun-sqlite"; +import type { BunextPageServerFn } from "@moduletrace/bunext/types"; + +const server: BunextPageServerFn = async ({ query }) => { + const page_query = query as PageQueryObject; + + const host_record_res = await BunSQLite.select< + BUN_SQLITE_WGUI_HOSTS, + TableType + >({ table: "hosts", targetId: page_query.host_id }); + + return { + props: { + host: host_record_res.singleRes || null, + }, + }; +}; + +export default server; diff --git a/src/pages/admin/hosts/[host_id]/clients/add-client/index.tsx b/src/pages/admin/hosts/[host_id]/clients/add-client/index.tsx new file mode 100644 index 0000000..3f941b4 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/add-client/index.tsx @@ -0,0 +1,29 @@ +import type { BunextPageModuleMeta } from "@moduletrace/bunext/types"; +import AdminHero from "@/src/components/general/admin-hero"; +import { SiteData } from "@/src/data/site-data"; +import Divider from "@/src/components/twui/layout/Divider"; +import { useContext } from "react"; +import { AppContext } from "@/src/pages/__root"; +import AddClientFormSection from "./(sections)/add-client-form-section"; + +export default function AdminSingleHostPage() { + const { pageProps } = useContext(AppContext); + + return ( + <> + + + + + + + ); +} + +export const meta: BunextPageModuleMeta = { + title: `Add new Client | ${SiteData["SiteName"]}`, + description: `WireGuard add new host client page`, +}; diff --git a/src/pages/admin/hosts/[host_id]/clients/index.server.ts b/src/pages/admin/hosts/[host_id]/clients/index.server.ts new file mode 100644 index 0000000..f230433 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/index.server.ts @@ -0,0 +1,43 @@ +import type { + BUN_SQLITE_WGUI_CLIENTS, + BUN_SQLITE_WGUI_HOSTS, +} from "@/db/types/db"; +import type { PagePropsType, PageQueryObject, TableType } from "@/src/types"; +import BunSQLite from "@moduletrace/bun-sqlite"; +import type { BunextPageServerFn } from "@moduletrace/bunext/types"; + +const server: BunextPageServerFn = async ({ + req, + url, + query, +}) => { + const page_query = query as PageQueryObject; + + const host_record_res = await BunSQLite.select< + BUN_SQLITE_WGUI_HOSTS, + TableType + >({ table: "hosts", targetId: page_query.host_id }); + + const host_clients = await BunSQLite.select< + BUN_SQLITE_WGUI_CLIENTS, + TableType + >({ + table: "clients", + query: { + query: { + host_id: { + value: page_query.host_id, + }, + }, + }, + }); + + return { + props: { + host: host_record_res.singleRes || null, + clients: host_clients.payload || null, + }, + }; +}; + +export default server; diff --git a/src/pages/admin/hosts/[host_id]/clients/index.tsx b/src/pages/admin/hosts/[host_id]/clients/index.tsx new file mode 100644 index 0000000..45c3cfd --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/clients/index.tsx @@ -0,0 +1,43 @@ +import type { BunextPageModuleMeta } from "@moduletrace/bunext/types"; +import AdminHero from "@/src/components/general/admin-hero"; +import { SiteData } from "@/src/data/site-data"; +import Button from "@/src/components/twui/layout/Button"; +import Divider from "@/src/components/twui/layout/Divider"; +import { useContext } from "react"; +import { AppContext } from "@/src/pages/__root"; +import ClientsListSection from "./(sections)/clients-list-section"; + +export default function AdminSingleHostPage() { + const { pageProps } = useContext(AppContext); + + const host_id = pageProps?.host?.id || 0; + + return ( + <> + + + + } + /> + + + + + + ); +} + +export const meta: BunextPageModuleMeta = { + title: `Host Clients | ${SiteData["SiteName"]}`, + description: `WireGuard host clients page`, +}; diff --git a/src/pages/admin/hosts/[host_id]/index.server.ts b/src/pages/admin/hosts/[host_id]/index.server.ts new file mode 100644 index 0000000..f230433 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/index.server.ts @@ -0,0 +1,43 @@ +import type { + BUN_SQLITE_WGUI_CLIENTS, + BUN_SQLITE_WGUI_HOSTS, +} from "@/db/types/db"; +import type { PagePropsType, PageQueryObject, TableType } from "@/src/types"; +import BunSQLite from "@moduletrace/bun-sqlite"; +import type { BunextPageServerFn } from "@moduletrace/bunext/types"; + +const server: BunextPageServerFn = async ({ + req, + url, + query, +}) => { + const page_query = query as PageQueryObject; + + const host_record_res = await BunSQLite.select< + BUN_SQLITE_WGUI_HOSTS, + TableType + >({ table: "hosts", targetId: page_query.host_id }); + + const host_clients = await BunSQLite.select< + BUN_SQLITE_WGUI_CLIENTS, + TableType + >({ + table: "clients", + query: { + query: { + host_id: { + value: page_query.host_id, + }, + }, + }, + }); + + return { + props: { + host: host_record_res.singleRes || null, + clients: host_clients.payload || null, + }, + }; +}; + +export default server; diff --git a/src/pages/admin/hosts/[host_id]/index.tsx b/src/pages/admin/hosts/[host_id]/index.tsx new file mode 100644 index 0000000..88effb1 --- /dev/null +++ b/src/pages/admin/hosts/[host_id]/index.tsx @@ -0,0 +1,30 @@ +import type { BunextPageModuleMeta } from "@moduletrace/bunext/types"; +import AdminHero from "@/src/components/general/admin-hero"; +import { SiteData } from "@/src/data/site-data"; +import Button from "@/src/components/twui/layout/Button"; +import Divider from "@/src/components/twui/layout/Divider"; + +export default function AdminSingleHostPage() { + return ( + <> + + + + } + /> + + + + ); +} + +export const meta: BunextPageModuleMeta = { + title: `Host | ${SiteData["SiteName"]}`, + description: `WireGuard host page`, +}; diff --git a/src/pages/admin/hosts/index.tsx b/src/pages/admin/hosts/index.tsx index 06d9220..f14cfbd 100644 --- a/src/pages/admin/hosts/index.tsx +++ b/src/pages/admin/hosts/index.tsx @@ -12,6 +12,8 @@ import EmptyContent from "@/src/components/twui/elements/EmptyContent"; import Span from "@/src/components/twui/layout/Span"; import checkIfMainHostIsSet from "@/src/functions/check-if-main-host-is-set"; import SetupMainHostButton from "@/src/components/general/setup-main-host-button"; +import Row from "@/src/components/twui/layout/Row"; +import { Plus } from "lucide-react"; export default function AdminHostPage() { const { hosts, variables, clients } = useHostData(); @@ -41,7 +43,9 @@ export default function AdminHostPage() { description="WireGuard server configuration and interface details" buttons={ <> - + } /> @@ -49,7 +53,20 @@ export default function AdminHostPage() { -

Main Host

+ +

Main Host

+ + + + +
| null; }; export type AppContextObject = { @@ -283,6 +291,7 @@ export type UserFormObject = BUN_SQLITE_WGUI_USERS & { export type PageQueryObject = { user_id?: string; + host_id?: string; event_id?: string; user_type?: UserType; }; diff --git a/src/types/sql-joins.ts b/src/types/sql-joins.ts index 5960bc0..add61fc 100644 --- a/src/types/sql-joins.ts +++ b/src/types/sql-joins.ts @@ -1,8 +1,10 @@ import type { + BUN_SQLITE_WGUI_HOSTS, BUN_SQLITE_WGUI_MEDIA, BUN_SQLITE_WGUI_MEDIA_PARADIGMS, BUN_SQLITE_WGUI_USERS, } from "@/db/types/db"; +import type grabHostDirnames from "../functions/backend/setup/grab-host-dir-names"; export type BUN_SQLITE_WGUI_USERS_JOIN = BUN_SQLITE_WGUI_USERS & { user_types?: string; @@ -20,3 +22,7 @@ export type BUN_SQLITE_WGUI_MEDIA_JOIN = BUN_SQLITE_WGUI_MEDIA & { media_paradigms?: string; user?: BUN_SQLITE_WGUI_USERS_JOIN; }; + +export type BUN_SQLITE_WGUI_HOSTS_JOIN = BUN_SQLITE_WGUI_HOSTS & { + dir_names?: ReturnType; +};