Refactor main host setup

This commit is contained in:
2026-09-20 15:37:57 +01:00
parent 3c1d2b61f6
commit 69111e461e
47 changed files with 669 additions and 591 deletions
+1 -1
View File
@@ -175,7 +175,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_public_ip_address" | "main_host_wg_public_key" | "main_host_wg_private_key" | "main_host_public_interface" | "";
key?: "test_var" | "";
value?: string;
}
@@ -0,0 +1,50 @@
import { useState } from "react";
import { Trash2 } from "lucide-react";
import Button from "@/src/components/twui/layout/Button";
import fetchApi from "@/src/components/twui/utils/fetch/fetchApi";
import type { ApiReqParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export default function DeleteHostButton({ host_id }: { host_id: number }) {
const [deleting, setDeleting] = useState(false);
async function handleDelete() {
if (
!window.confirm(
`Delete host #${host_id}? All clients and their configurations will be removed.`,
)
) {
return;
}
setDeleting(true);
const res = await fetchApi<ApiReqParams, APIResponseObject>(
`/api/admin/delete-host`,
{
method: "DELETE",
body: { host_id },
},
);
if (!res.success) {
setDeleting(false);
window.alert(res.msg || "Could not delete the host");
return;
}
window.location.pathname = "/admin/hosts";
}
return (
<Button
title="Delete Host"
size="small"
variant="ghost"
color="error"
beforeIcon={<Trash2 size={14} />}
disabled={deleting}
onClick={handleDelete}
/>
);
}
+1 -1
View File
@@ -17,7 +17,7 @@ export const AppData = {
ImageThumbnailQuality: 70,
PaystackEndpoint: "https://api.paystack.co",
WireguardHostID: 0,
WireguardMainHostID: 0,
DefaultPrivateIP: `10.1.0.1`,
DefaultWGListenPort: 51820,
} as const;
+28 -23
View File
@@ -1,27 +1,32 @@
export const Variables = [
{
title: `Main Host Wireguard IP Address`,
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",
description: `Public Key for the main host`,
},
{
title: `Main Host Wireguard Private Key`,
value: "main_host_wg_private_key",
description: `Private Key for the main host`,
},
{
title: `Main Host Public Interface`,
value: "main_host_public_interface",
description: `Network interface for the main host (e.g. eth0, wlan0)`,
title: `Test Variable`,
value: "test_var",
description: `Just a test variable to define the schema`,
},
// {
// title: `Main Host Wireguard IP Address`,
// 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",
// description: `Public Key for the main host`,
// },
// {
// title: `Main Host Wireguard Private Key`,
// value: "main_host_wg_private_key",
// description: `Private Key for the main host`,
// },
// {
// title: `Main Host Public Interface`,
// value: "main_host_public_interface",
// description: `Network interface for the main host (e.g. eth0, wlan0)`,
// },
] as const;
@@ -98,10 +98,10 @@ function acceptLines({
const port_chunks = ports[0] ? chunkPorts({ ports }) : [[]];
for (let p = 0; p < protocols.length; p++) {
const proto = protocols[p];
const proto = protocols[p];
for (let c = 0; c < port_chunks.length; c++) {
const chunk = port_chunks[c] || [];
for (let c = 0; c < port_chunks.length; c++) {
const chunk = port_chunks[c] || [];
lines.push(
joinArgs([
@@ -1,12 +1,10 @@
import type {
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import type { TableType, User } from "@/src/types";
import setupWireguardHost from "./setup-wireguard-host";
import grabHostPublicIPAddress from "./grab-host-public-ip-address";
import { AppData } from "@/src/data/app-data";
type Params = {
wg_ip_address?: string | null;
@@ -64,10 +62,10 @@ export default async function createWireguardHost({
};
if (is_main_host) {
insert_data.id = 0;
insert_data.id = AppData["WireguardMainHostID"];
}
const insert_host = await BunSQLite.insert<
const insert_host_res = await BunSQLite.insert<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
@@ -76,15 +74,17 @@ export default async function createWireguardHost({
update_on_duplicate: true,
});
if (!insert_host.success || !insert_host.postInsertReturn?.insertId) {
const host_id = is_main_host
? AppData["WireguardMainHostID"]
: insert_host_res.postInsertReturn?.insertId;
if (typeof host_id !== "number") {
return {
success: false,
msg: `Could not create the host record: ${insert_host.msg}`,
msg: `Could not create the host record: ${insert_host_res.msg}`,
};
}
const host_id = Number(insert_host.postInsertReturn?.insertId);
const host: BUN_SQLITE_WGUI_HOSTS = {
id: host_id,
user_id: user.id,
@@ -96,19 +96,6 @@ export default async function createWireguardHost({
listen_port: listen_port != null ? listen_port : undefined,
};
if (host_id === 0 && final_interface) {
await BunSQLite.insert<BUN_SQLITE_WGUI_VARIABLES, TableType>({
table: "variables",
data: [
{
key: "main_host_public_interface",
value: final_interface,
},
],
update_on_duplicate: true,
});
}
const setup_res = await setupWireguardHost({
host,
wg_subnet_ip: wg_ip,
@@ -0,0 +1,96 @@
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_CLIENT_RULES,
BUN_SQLITE_WGUI_HOSTS,
} from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import type { TableType, User } from "@/src/types";
import manageWireguardHost from "./manage-wireguard-host";
import grabHostDirnames from "./grab-host-dir-names";
import grabClientDirnames from "./grab-client-dir-names";
import { execSync } from "node:child_process";
type Params = {
host_id: number;
user: User;
};
export default async function deleteWireguardHost({
host_id,
user,
}: Params): Promise<APIResponseObject> {
try {
const host_clients_res = await BunSQLite.select<
BUN_SQLITE_WGUI_CLIENTS,
TableType
>({
table: "clients",
query: {
query: {
host_id: { value: String(host_id) },
},
},
});
const clients = host_clients_res.payload || [];
for (let i = 0; i < clients.length; i++) {
const client = clients[i];
if (!client?.id) continue;
await BunSQLite.delete<BUN_SQLITE_WGUI_CLIENT_RULES, TableType>({
table: "client_rules",
query: {
query: {
client_id: { value: String(client.id) },
},
},
});
const { CLIENT_DIR } = grabClientDirnames({
client,
host_id,
});
try {
execSync(`rm -rf ${CLIENT_DIR}`, { encoding: "utf-8" });
} catch {}
await BunSQLite.delete<BUN_SQLITE_WGUI_CLIENTS, TableType>({
table: "clients",
targetId: client.id,
});
}
const { HOST_CONFIG_DIR, INTERFACE_NAME } = grabHostDirnames({
host: { id: host_id },
});
try {
manageWireguardHost({
action: "down",
host_id,
});
} catch {}
try {
execSync(`rm -rf ${HOST_CONFIG_DIR}`, { encoding: "utf-8" });
} catch {}
const del_host = await BunSQLite.sql({
sql: `DELETE FROM HOSTS WHERE id=?`,
values: [host_id],
});
return {
success: true,
msg: `Host ${host_id} deleted`,
};
} catch (error: any) {
return {
success: false,
msg: error.message,
};
}
}
@@ -17,7 +17,7 @@ const {
} = grabDirNames();
export default function grabHostDirnames({ host }: Params) {
const HOST_ID = host?.id || AppData["WireguardHostID"];
const HOST_ID = host?.id || AppData["WireguardMainHostID"];
const INTERFACE_NAME = deriveWireguardInterfaceName({ host_id: HOST_ID });
@@ -1,7 +1,6 @@
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import BunSQLite from "@moduletrace/bun-sqlite";
@@ -21,18 +20,7 @@ type Params = {
export default async function grabNextAvailableClientIPAddress({
host,
}: Params): Promise<APIResponseObject> {
const variables_res = await BunSQLite.select<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
});
const variables = variables_res.payload;
const HOST_WG_IP =
host?.wg_ip_address ||
variables?.find((v) => v.key == "main_host_wg_ip_address")?.value;
const HOST_WG_IP = host?.wg_ip_address;
if (!HOST_WG_IP) {
return {
@@ -97,6 +85,6 @@ export default async function grabNextAvailableClientIPAddress({
return {
success: true,
msg: `${subnet}${next_last_octet}`,
stringRes: `${subnet}${next_last_octet}`,
};
}
@@ -18,7 +18,7 @@ type Params = {
export default function manageWireguardHost({
action,
host_id = AppData["WireguardHostID"],
host_id = AppData["WireguardMainHostID"],
}: Params): APIResponseObject {
const { HOST_CONFIG_FILE, INTERFACE_NAME } = grabHostDirnames({
host: { id: host_id },
@@ -1,7 +1,6 @@
import type {
BUN_SQLITE_WGUI_CLIENTS,
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";
@@ -42,16 +41,20 @@ export default async function setupWireguardClient({
user,
skip_host_setup,
}: Params): Promise<APIResponseObject> {
const host_id = host?.id || AppData["WireguardHostID"];
const host_id = host?.id || AppData["WireguardMainHostID"];
const variables_res = await BunSQLite.select<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
});
let resolved_host = host;
const variables = variables_res.payload;
if (!resolved_host) {
const host_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: host_id,
});
resolved_host = host_res.singleRes || undefined;
}
const {
CLIENT_WG_IP,
@@ -69,9 +72,7 @@ export default async function setupWireguardClient({
};
}
const HOST_WG_IP = host?.id
? host?.wg_ip_address
: variables?.find((v) => v.key == "main_host_wg_ip_address")?.value;
const HOST_WG_IP = resolved_host?.wg_ip_address;
if (!HOST_WG_IP) {
return {
@@ -98,9 +99,7 @@ export default async function setupWireguardClient({
try {
const PUBLIC_IP_ADDRESS =
host?.public_ip_address ||
variables?.find((v) => v.key == "main_host_public_ip_address")
?.value ||
resolved_host?.public_ip_address ||
(await grabHostPublicIPAddress()) ||
client?.public_ip_address ||
HOST_WG_IP;
@@ -130,7 +129,7 @@ export default async function setupWireguardClient({
sh += `\n`;
sh += `[Peer]\n`;
sh += `PublicKey = ${HOST_PUBLIC_KEY}\n`;
sh += `Endpoint = ${PUBLIC_IP_ADDRESS}:${host?.listen_port || AppData["DefaultWGListenPort"]}\n`;
sh += `Endpoint = ${PUBLIC_IP_ADDRESS}:${resolved_host?.listen_port || AppData["DefaultWGListenPort"]}\n`;
if (client?.allow_all_ips == 1) {
sh += `AllowedIPs = 0.0.0.0/0, ::/0\n`;
@@ -154,7 +153,7 @@ export default async function setupWireguardClient({
const host_setup_res = skip_host_setup
? { success: true }
: await setupWireguardHost({
host,
host: resolved_host,
user,
is_update_after_client_setup: true,
});
@@ -2,7 +2,6 @@ import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_CLIENT_RULES,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import grabDirNames from "@/src/utils/grab-dir-names";
import { execSync } from "node:child_process";
@@ -46,15 +45,6 @@ export default async function setupWireguardHost({
POST_UP_PATH,
} = grabHostDirnames({ host });
const variables_res = await BunSQLite.select<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
});
const variables = variables_res.payload;
const host_clients_res = await BunSQLite.select<
BUN_SQLITE_WGUI_CLIENTS,
TableType
@@ -103,10 +93,7 @@ export default async function setupWireguardHost({
const TARGET_INTERFACE =
host?.interface || (await grabHostNetworkInterface());
const HOST_WG_IP =
host?.wg_ip_address ||
variables?.find((v) => v.key == "main_host_wg_ip_address")?.value ||
wg_subnet_ip;
const HOST_WG_IP = host?.wg_ip_address || wg_subnet_ip;
if (!HOST_WG_IP) {
return {
@@ -159,37 +146,7 @@ export default async function setupWireguardHost({
encoding: "utf-8",
}).trim();
if (HOST_ID == 0 && !is_update_after_client_setup) {
const variables_data: Array<{ key: string; value: string }> = [
{
key: "main_host_wg_ip_address",
value: HOST_WG_IP,
},
{
key: "main_host_wg_public_key",
value: HOST_PUBLIC_KEY,
},
];
if (host?.interface) {
variables_data.push({
key: "main_host_public_interface",
value: host.interface,
});
}
const update_variables = await BunSQLite.insert<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
data: variables_data as BUN_SQLITE_WGUI_VARIABLES[],
update_on_duplicate: true,
});
if (!update_variables.success) {
throw new Error(`Couldn't update host variables`);
}
} else if (HOST_ID && !is_update_after_client_setup) {
if (!is_update_after_client_setup) {
const update_host = await BunSQLite.insert<
BUN_SQLITE_WGUI_HOSTS,
TableType
@@ -2,12 +2,8 @@ import grabDirNames from "@/src/utils/grab-dir-names";
import fs from "node:fs";
import manageWireguardHost from "./manage-wireguard-host";
import BunSQLite from "@moduletrace/bun-sqlite";
import type {
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import type { TableType } from "@/src/types";
import { AppData } from "@/src/data/app-data";
import bunext from "@moduletrace/bunext";
const HOST_CONFIG_FILE_NAME_PATTERN = /^wgui(\d+)\.conf$/;
@@ -24,44 +20,22 @@ export default async function syncWireguardHosts() {
HOST_CONFIG_FILE_NAME_PATTERN.test(file_name),
);
const variables = await BunSQLite.select<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
});
const hosts = await BunSQLite.select<BUN_SQLITE_WGUI_HOSTS, TableType>({
table: "hosts",
});
const main_host_ip = variables.payload?.find(
(v) => v.key == "main_host_wg_ip_address",
)?.value;
const main_host = hosts.payload?.find((h) => h.id === 0);
const main_host_public_ip = variables.payload?.find(
(v) => v.key == "main_host_public_ip_address",
)?.value;
const main_host_interface = variables.payload?.find(
(v) => v.key == "main_host_public_interface",
)?.value;
if (!main_host_ip) {
if (!main_host?.wg_ip_address) {
throw new Error(`Main Host not set yet`);
}
const all_hosts: BUN_SQLITE_WGUI_HOSTS[] = [
{
id: 0,
public_ip_address: main_host_public_ip,
wg_ip_address: main_host_ip,
interface: main_host_interface,
},
...(hosts.payload || []),
const sorted_hosts = [
main_host,
...(hosts.payload || []).filter((h) => h.id !== 0),
];
for (const host of all_hosts) {
for (const host of sorted_hosts) {
if (typeof host.id == "number") {
const res = manageWireguardHost({
action: "up",
@@ -1,9 +1,4 @@
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import { AppData } from "@/src/data/app-data";
import type { BUN_SQLITE_WGUI_CLIENTS, BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import type { TableType, User } from "@/src/types";
@@ -29,7 +24,7 @@ export default async function updateWireguardHostPublicIP({
public_ip_address,
user,
}: Params): Promise<APIResponseObject> {
const final_host_id = Number(host_id || AppData["WireguardHostID"]);
const final_host_id = Number(host_id || 0);
const ip = (public_ip_address || "").trim();
if (!ip || !IP_V4_REGEX.test(ip)) {
@@ -42,65 +37,43 @@ export default async function updateWireguardHostPublicIP({
let host: BUN_SQLITE_WGUI_HOSTS | undefined;
try {
if (final_host_id == AppData["WireguardHostID"]) {
const update_variables = await BunSQLite.insert<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
data: [
{
key: "main_host_public_ip_address",
value: ip,
},
],
update_on_duplicate: true,
});
const host_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: final_host_id,
});
// console.log("update_variables", update_variables);
host = host_res.singleRes || undefined;
// if (!update_variables.success) {
// throw new Error(`Couldn't update host public IP variable`);
// }
} else {
const host_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: final_host_id,
});
host = host_res.singleRes || undefined;
if (!host) {
return {
success: false,
msg: `Host record not found`,
};
}
const update_host = await BunSQLite.insert<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
data: [
{
id: host.id,
user_id: user.id,
public_ip_address: ip,
},
],
update_on_duplicate: true,
});
if (!update_host.success) {
throw new Error(`Couldn't update host record`);
}
host.public_ip_address = ip;
if (!host) {
return {
success: false,
msg: `Host record not found`,
};
}
const update_host = await BunSQLite.insert<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
data: [
{
id: host.id,
user_id: user.id,
public_ip_address: ip,
},
],
update_on_duplicate: true,
});
if (!update_host.success) {
throw new Error(`Couldn't update host record`);
}
host.public_ip_address = ip;
} catch (error: any) {
return {
success: false,
@@ -1,7 +1,4 @@
import type {
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import { AppData } from "@/src/data/app-data";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
@@ -20,9 +17,6 @@ type Params = {
/**
* Update the private WireGuard subnet IP of a host
* and regenerate the host config + restart the tunnel.
*
* For the main host (id 0) the value is stored in the
* variables table, otherwise the hosts record is updated.
* @param param0
* @returns
*/
@@ -31,7 +25,7 @@ export default async function updateWireguardHost({
wg_ip_address,
user,
}: Params): Promise<APIResponseObject> {
const final_host_id = Number(host_id || AppData["WireguardHostID"]);
const final_host_id = Number(host_id || AppData["WireguardMainHostID"]);
const ip = (wg_ip_address || "").trim();
if (!ip || !IP_V4_REGEX.test(ip)) {
@@ -45,37 +39,24 @@ export default async function updateWireguardHost({
let current_wg_ip: string | undefined;
try {
if (final_host_id == AppData["WireguardHostID"]) {
const variables_res = await BunSQLite.select<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
});
const host_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: final_host_id,
});
current_wg_ip = variables_res.payload?.find(
(v) => v.key == "main_host_wg_ip_address",
)?.value;
} else {
const host_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: final_host_id,
});
host = host_res.singleRes || undefined;
host = host_res.singleRes || undefined;
if (!host) {
return {
success: false,
msg: `Host record not found`,
};
}
current_wg_ip = host.wg_ip_address || undefined;
if (!host) {
return {
success: false,
msg: `Host record not found`,
};
}
current_wg_ip = host.wg_ip_address || undefined;
} catch (error: any) {
return {
success: false,
@@ -97,53 +78,27 @@ export default async function updateWireguardHost({
}
try {
if (final_host_id == AppData["WireguardHostID"]) {
const update_variables = await BunSQLite.insert<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
data: [
{
key: "main_host_wg_ip_address",
value: ip,
},
],
update_on_duplicate: true,
});
const update_host = await BunSQLite.insert<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
data: [
{
id: host?.id,
user_id: user.id,
wg_ip_address: ip,
},
],
update_on_duplicate: true,
});
if (!update_variables.success) {
throw new Error(`Couldn't update host variables`);
}
if (!update_host.success) {
throw new Error(`Couldn't update host record`);
}
host = {
id: AppData["WireguardHostID"],
user_id: user.id,
wg_ip_address: ip,
};
} else {
const update_host = await BunSQLite.insert<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
data: [
{
id: host?.id,
user_id: user.id,
wg_ip_address: ip,
},
],
update_on_duplicate: true,
});
if (!update_host.success) {
throw new Error(`Couldn't update host record`);
}
if (host) {
host.wg_ip_address = ip;
}
if (host) {
host.wg_ip_address = ip;
}
} catch (error: any) {
return {
+4 -12
View File
@@ -1,17 +1,9 @@
import type { BUN_SQLITE_WGUI_VARIABLES } from "@/db/types/db";
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
type Params = {
variables?: BUN_SQLITE_WGUI_VARIABLES[];
host?: BUN_SQLITE_WGUI_HOSTS;
};
export default function checkIfMainHostIsSet({ variables }: Params) {
const is_main_host_set =
Boolean(
variables?.find((v) => v.key == "main_host_wg_ip_address")?.value,
) &&
Boolean(
variables?.find((v) => v.key == "main_host_wg_public_key")?.value,
);
return is_main_host_set;
export default function checkIfMainHostIsSet({ host }: Params) {
return Boolean(host?.wg_ip_address);
}
+15 -1
View File
@@ -1,7 +1,10 @@
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
import type { PagePropsType } from "../types";
import type { PagePropsType, TableType } from "../types";
import userAuth from "../functions/backend/auth/user-auth";
import grabHostDirnames from "../functions/backend/setup/grab-host-dir-names";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import { AppData } from "../data/app-data";
const server: BunextPageServerFn<PagePropsType> = async ({ req, url }) => {
const { user, user_types } = await userAuth({ req });
@@ -30,6 +33,16 @@ const server: BunextPageServerFn<PagePropsType> = async ({ req, url }) => {
const main_host_dir_names = grabHostDirnames({});
const main_host_record_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: AppData["WireguardMainHostID"],
});
const main_host_record = main_host_record_res.singleRes || null;
return {
props: {
environment: process.env.NODE_ENV,
@@ -40,6 +53,7 @@ const server: BunextPageServerFn<PagePropsType> = async ({ req, url }) => {
user,
user_types,
main_host_dir_names,
main_host: main_host_record,
},
};
};
+1 -10
View File
@@ -1,9 +1,5 @@
import { useAdminCrudGet } from "@/src/hooks/use-admin-crud-get";
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_CLIENTS, BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
export default function useDashboardData() {
const clients_res = useAdminCrudGet<BUN_SQLITE_WGUI_CLIENTS>({
@@ -19,13 +15,8 @@ export default function useDashboardData() {
limit: 100,
});
const variables_res = useAdminCrudGet<BUN_SQLITE_WGUI_VARIABLES>({
table: "variables",
});
return {
clients: clients_res.res,
hosts: hosts_res.res,
variables: variables_res.res,
};
}
@@ -2,20 +2,15 @@ import StatCard from "../(partials)/stat-card";
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
type Props = {
clients?: BUN_SQLITE_WGUI_CLIENTS[];
hosts?: BUN_SQLITE_WGUI_HOSTS[];
variables?: BUN_SQLITE_WGUI_VARIABLES[];
};
export default function KpiSecondarySection({
clients,
hosts,
variables,
}: Props) {
export default function KpiSecondarySection({ clients, hosts }: Props) {
const main_host = hosts?.find((h) => h.id === 0);
const kpis = [
{
key: "k-clients",
@@ -25,14 +20,12 @@ export default function KpiSecondarySection({
{
key: "k-hosts",
label: "Total hosts",
value: String(hosts?.length || 1),
value: String(hosts?.length),
},
{
key: "k-host-address",
label: "Main host address",
value:
variables?.find((v) => v.key == "main_host_wg_ip_address")
?.value || "—",
value: main_host?.wg_ip_address || "—",
},
];
+32
View File
@@ -0,0 +1,32 @@
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import { AppData } from "@/src/data/app-data";
import type { PagePropsType, PageQueryObject, TableType } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
const server: BunextPageServerFn<PagePropsType> = async ({ query }) => {
const main_host_record_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: AppData["WireguardMainHostID"],
});
const main_host_record = main_host_record_res.singleRes || null;
if (!main_host_record?.name) {
return {
redirect: {
destination: `/admin/hosts/add`,
permanent: false,
},
};
}
return {
props: {},
};
};
export default server;
@@ -1,27 +1,18 @@
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_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
import type { BUN_SQLITE_WGUI_HOSTS_JOIN } from "@/src/types/sql-joins";
type Params = {
host?: BUN_SQLITE_WGUI_HOSTS_JOIN;
variables?: BUN_SQLITE_WGUI_VARIABLES[];
clients?: BUN_SQLITE_WGUI_CLIENTS[];
};
export default function deriveHostConfig({ host, variables, clients }: Params) {
const host_id = host?.id || AppData["WireguardHostID"];
export default function deriveHostConfig({ host, clients }: Params) {
const host_id = host?.id || AppData["WireguardMainHostID"];
const wg_ip_address =
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 wg_ip_address = host?.wg_ip_address;
const public_key = host?.public_key;
const interface_name = deriveWireguardInterfaceName({ host_id });
@@ -2,27 +2,27 @@ import { useAdminCrudGet } from "@/src/hooks/use-admin-crud-get";
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import { useContext } from "react";
import { AppContext } from "@/src/pages/__root";
export default function useHostData() {
const { pageProps } = useContext(AppContext);
const hosts_res = useAdminCrudGet<BUN_SQLITE_WGUI_HOSTS>({
table: "hosts",
limit: 100,
});
const variables_res = useAdminCrudGet<BUN_SQLITE_WGUI_VARIABLES>({
table: "variables",
initial_res: pageProps?.hosts || undefined,
});
const clients_res = useAdminCrudGet<BUN_SQLITE_WGUI_CLIENTS>({
table: "clients",
limit: 100,
initial_res: pageProps?.clients || undefined,
});
return {
hosts: hosts_res.res,
variables: variables_res.res,
clients: clients_res.res,
};
}
@@ -1,28 +1,32 @@
import { Server } from "lucide-react";
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import { GlobeCheck, Server } from "lucide-react";
import AdminCard from "@/src/components/general/admin-card";
import DeleteHostButton from "@/src/components/general/delete-host-button";
import Button from "@/src/components/twui/layout/Button";
import Row from "@/src/components/twui/layout/Row";
import Span from "@/src/components/twui/layout/Span";
import Stack from "@/src/components/twui/layout/Stack";
import Tag from "@/src/components/twui/elements/Tag";
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import { AppData } from "@/src/data/app-data";
type Props = {
host: BUN_SQLITE_WGUI_HOSTS;
client_count?: number;
};
export default function OtherHostCard({ host, client_count }: Props) {
export default function HostCard({ host, client_count }: Props) {
const host_id = host.id || 0;
const title = host_id == 0 ? "Main Host" : ` Host #${host_id}`;
return (
<AdminCard className="w-full p-5 flex flex-col gap-4">
<Row className="justify-between items-center gap-3 flex-wrap">
<Stack className="gap-0.5">
<Stack className="gap-2">
<Row className="gap-2 items-center">
<Server size={15} className="opacity-50" />
<GlobeCheck size={15} className="opacity-50" />
<Span className="text-[14px] font-semibold leading-none">
Host #{host_id}
{title}
</Span>
</Row>
<Span className="font-mono text-[12.5px] text-foreground-light/45 dark:text-foreground-dark/45">
@@ -46,22 +50,30 @@ export default function OtherHostCard({ host, client_count }: Props) {
>
Edit
</Button>
<Button
title="Host Clients"
size="small"
variant="outlined"
href={`/admin/hosts/${host_id}/clients`}
>
Clients
</Button>
<DeleteHostButton host_id={host_id} />
</Row>
</Row>
<Row className="gap-2 items-center flex-wrap">
<Tag variant="outlined" color="gray">
{host.public_ip_address ? (
<Span size="smaller">
<code className="text-xs">
{host.public_ip_address} :{" "}
{host.listen_port || AppData["DefaultWGListenPort"]}
</code>
</Span>
) : null}
<Span size="smaller">
{client_count || 0}{" "}
{client_count == 1 ? "client" : "clients"}
</Tag>
{host.public_ip_address ? (
<Tag
variant="outlined"
color="success"
className="outline-success/50 bg-success/5!"
>
{host.public_ip_address}
</Tag>
) : null}
</Span>
</Row>
</AdminCard>
);
@@ -8,11 +8,7 @@ import Row from "@/src/components/twui/layout/Row";
import Span from "@/src/components/twui/layout/Span";
import Stack from "@/src/components/twui/layout/Stack";
import deriveHostConfig from "../(functions)/derive-host-config";
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_CLIENTS } 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";
@@ -21,13 +17,11 @@ import { AppContext } from "@/src/pages/__root";
type Props = {
host?: BUN_SQLITE_WGUI_HOSTS_JOIN;
variables?: BUN_SQLITE_WGUI_VARIABLES[];
clients?: BUN_SQLITE_WGUI_CLIENTS[];
};
export default function InterfaceConfigSection({
host,
variables,
clients,
}: Props) {
const { pageProps } = useContext(AppContext);
@@ -38,7 +32,7 @@ export default function InterfaceConfigSection({
dir_names: pageProps?.main_host_dir_names || undefined,
};
const config = deriveHostConfig({ host: final_host, variables, clients });
const config = deriveHostConfig({ host: final_host, clients });
const rows = [
{ keyName: "Address", value: config.address || "—" },
@@ -5,20 +5,16 @@ import Span from "@/src/components/twui/layout/Span";
import Stack from "@/src/components/twui/layout/Stack";
import deriveHostConfig from "../(functions)/derive-host-config";
import { twMerge } from "tailwind-merge";
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
import type { BUN_SQLITE_WGUI_HOSTS_JOIN } from "@/src/types/sql-joins";
type Props = {
host?: BUN_SQLITE_WGUI_HOSTS_JOIN;
variables?: BUN_SQLITE_WGUI_VARIABLES[];
clients?: BUN_SQLITE_WGUI_CLIENTS[];
};
export default function MainHostStatusSection({ host, variables, clients }: Props) {
const config = deriveHostConfig({ host, variables, clients });
export default function MainHostStatusSection({ host, clients }: Props) {
const config = deriveHostConfig({ host, clients });
const stats = [
{
@@ -28,14 +28,12 @@ const server: BunextPageServerFn<PagePropsType> = async ({ query }) => {
const client = client_res.singleRes || null;
const host_id = Number(
client?.host_id ||
page_query.host_id ||
AppData["WireguardHostID"],
client?.host_id || page_query.host_id || AppData["WireguardMainHostID"],
);
let host: BUN_SQLITE_WGUI_HOSTS | null = null;
if (host_id && host_id != AppData["WireguardHostID"]) {
if (host_id && host_id != AppData["WireguardMainHostID"]) {
const host_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
@@ -21,7 +21,7 @@ export default async function grabNextAvailableClientIp({
},
);
if (!res?.success || !res.msg) {
if (!res?.success || !res.stringRes) {
return {
success: false,
msg: res?.msg || `Couldn't grab the next available client IP`,
@@ -30,8 +30,8 @@ export default async function grabNextAvailableClientIp({
setForm((prev) => ({
...prev,
wg_ip_address: res.msg,
allowed_ips: `${res.msg}/24`,
wg_ip_address: res.stringRes || "",
allowed_ips: `${res.stringRes}/24`,
}));
return {
@@ -24,6 +24,7 @@ export default async function submitAddClientForm(
form,
existing_full,
app_context,
clearLocalForm,
}: ReturnType<typeof useFormInit<BUN_SQLITE_WGUI_CLIENTS>>,
{ host_id, rules }: Params,
) {
@@ -113,6 +114,8 @@ export default async function submitAddClientForm(
}
if (res.success) {
clearLocalForm();
if (existing_full?.id) {
window.location.pathname = `/admin/hosts/${final_host_id}/clients/${existing_full.id}`;
} else {
@@ -38,6 +38,15 @@ export default function AddClientForm({
existing: existing_client,
existing_full: existing_client_full,
title: "Add Client",
async before_ready_function(params) {
params.setForm((prev) => ({
...prev,
wg_ip_address:
existing_client_full?.wg_ip_address ||
params.app_context.pageProps?.next_available_client_ip ||
"",
}));
},
});
const [rules, setRules] = useState<ClientRuleDraft[]>(
@@ -1,4 +1,5 @@
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import grabNextAvailableClientIPAddress from "@/src/functions/backend/setup/grab-next-available-client-ip";
import type { PagePropsType, PageQueryObject, TableType } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
@@ -11,9 +12,27 @@ const server: BunextPageServerFn<PagePropsType> = async ({ query }) => {
TableType
>({ table: "hosts", targetId: page_query.host_id });
const host_record = host_record_res.singleRes || null;
if (!host_record) {
return {
redirect: {
destination: `/admin/hosts`,
},
};
}
const next_available_client_ip_res = await grabNextAvailableClientIPAddress(
{
host: host_record,
},
);
return {
props: {
host: host_record_res.singleRes || null,
host: host_record,
next_available_client_ip:
next_available_client_ip_res.stringRes || null,
},
};
};
@@ -1,8 +1,4 @@
import type {
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import { AppData } from "@/src/data/app-data";
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";
@@ -10,9 +6,7 @@ import type { BunextPageServerFn } from "@moduletrace/bunext/types";
const server: BunextPageServerFn<PagePropsType> = async ({ query }) => {
const page_query = query as PageQueryObject;
const host_id = Number(
page_query.host_id || AppData["WireguardHostID"],
);
const host_id = Number(page_query.host_id || 0);
const host_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
@@ -22,31 +16,7 @@ const server: BunextPageServerFn<PagePropsType> = async ({ query }) => {
targetId: host_id,
});
let host = host_res.singleRes || null;
if (!host && host_id == AppData["WireguardHostID"]) {
const variables_res = await BunSQLite.select<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({
table: "variables",
});
const variables = variables_res.payload || [];
host = {
id: AppData["WireguardHostID"],
wg_ip_address:
variables.find((v) => v.key == "main_host_wg_ip_address")
?.value || "",
public_ip_address:
variables.find((v) => v.key == "main_host_public_ip_address")
?.value || "",
public_key:
variables.find((v) => v.key == "main_host_wg_public_key")
?.value || "",
};
}
const host = host_res.singleRes || null;
return {
props: {
@@ -1,8 +1,4 @@
import type {
BUN_SQLITE_WGUI_CLIENTS,
BUN_SQLITE_WGUI_HOSTS,
BUN_SQLITE_WGUI_VARIABLES,
} from "@/db/types/db";
import type { BUN_SQLITE_WGUI_CLIENTS, BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import grabHostDirnames from "@/src/functions/backend/setup/grab-host-dir-names";
import type { PagePropsType, PageQueryObject, TableType } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
@@ -43,16 +39,10 @@ const server: BunextPageServerFn<PagePropsType> = async ({
},
});
const variables_res = await BunSQLite.select<
BUN_SQLITE_WGUI_VARIABLES,
TableType
>({ table: "variables" });
return {
props: {
host,
clients: host_clients.payload || null,
variables: variables_res.payload || null,
},
};
};
+19 -33
View File
@@ -1,6 +1,7 @@
import type { BunextPageModuleMeta } from "@moduletrace/bunext/types";
import { useContext } from "react";
import AdminHero from "@/src/components/general/admin-hero";
import DeleteHostButton from "@/src/components/general/delete-host-button";
import { SiteData } from "@/src/data/site-data";
import Button from "@/src/components/twui/layout/Button";
import Divider from "@/src/components/twui/layout/Divider";
@@ -17,11 +18,8 @@ export default function AdminSingleHostPage() {
const host_id = Number(query?.host_id || pageProps?.host?.id || 0);
const host = pageProps?.host || undefined;
const variables = pageProps?.variables || [];
const clients = pageProps?.clients || [];
const is_main_host = !host || host_id == 0;
if (!pageProps?.host && host_id != 0) {
return (
<>
@@ -37,13 +35,23 @@ export default function AdminSingleHostPage() {
);
}
const title = host_id == 0 ? "Main Host" : `Host #${host_id}`;
return (
<>
<AdminHero
title={is_main_host ? "Host" : `Host #${host_id}`}
title={host ? title : "Host"}
description="WireGuard server configuration and interface details"
buttons={
<>
<Button
title="Edit Host"
size="small"
variant="outlined"
href={`/admin/hosts/${host_id}/edit`}
>
Edit Host
</Button>
<Button
title="View Clients"
size="small"
@@ -54,21 +62,13 @@ export default function AdminSingleHostPage() {
View Clients
</Button>
<Button
title="Edit Host"
title="Add Client"
size="small"
variant="outlined"
href={`/admin/hosts/${host_id}/edit`}
href={`/admin/hosts/${host_id}/clients/add-client`}
>
Edit Host
</Button>
<Button
title="Add New Host"
size="small"
variant="outlined"
href="/admin/hosts/add"
>
Add New Host
Add Client
</Button>
<DeleteHostButton host_id={host_id} />
</>
}
/>
@@ -76,26 +76,12 @@ export default function AdminSingleHostPage() {
<Divider className="mb-6" />
<Stack className="w-full px-6 pb-8 gap-5 items-stretch">
<MainHostStatusSection
host={host}
variables={variables}
clients={clients}
/>
<MainHostStatusSection host={host} clients={clients} />
<HostPublicIpSection
host_id={host_id}
current_ip={
host?.public_ip_address ||
variables.find(
(v) => v.key == "main_host_public_ip_address",
)?.value ||
""
}
/>
<InterfaceConfigSection
host={host}
variables={variables}
clients={clients}
current_ip={host?.public_ip_address || ""}
/>
<InterfaceConfigSection host={host} clients={clients} />
</Stack>
</>
);
@@ -8,6 +8,8 @@ export default async function submitAddHostForm({
setLoading,
setStatus,
form,
app_context,
clearLocalForm,
}: ReturnType<typeof useFormInit<BUN_SQLITE_WGUI_HOSTS>>) {
try {
if (!window.confirm("Create this new host?")) {
@@ -26,12 +28,17 @@ export default async function submitAddHostForm({
interface: form.interface || "",
name: form.name || "",
short_description: form.short_description || "",
listen_port: form.listen_port != null ? Number(form.listen_port) : null,
listen_port:
form.listen_port != null
? Number(form.listen_port)
: null,
is_main_host: app_context.pageProps?.is_main_host,
},
},
);
if (res.success && res.numberRes) {
if (res.success) {
clearLocalForm();
window.location.pathname = `/admin/hosts/${res.numberRes}`;
return;
}
@@ -20,6 +20,7 @@ export default function AddHostFormName({ form, setForm }: Props) {
name: v,
}));
}}
required
/>
</Stack>
);
+16 -1
View File
@@ -1,16 +1,31 @@
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import { AppData } from "@/src/data/app-data";
import getNextAvailableListenPort from "@/src/functions/backend/setup/get-next-available-listen-port";
import grabHostNetworkInterfaces from "@/src/functions/backend/setup/grab-host-network-interfaces";
import type { PagePropsType } from "@/src/types";
import type { PagePropsType, TableType } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
const server: BunextPageServerFn<PagePropsType> = async ({ req }) => {
const network_interfaces = await grabHostNetworkInterfaces();
const next_listen_port = await getNextAvailableListenPort();
const main_host_record_res = await BunSQLite.select<
BUN_SQLITE_WGUI_HOSTS,
TableType
>({
table: "hosts",
targetId: AppData["WireguardMainHostID"],
});
const main_host_record = main_host_record_res.singleRes || null;
return {
props: {
network_interfaces,
next_listen_port,
is_main_host:
main_host_record?.id !== AppData["WireguardMainHostID"],
},
};
};
+19 -2
View File
@@ -6,13 +6,30 @@ import Divider from "@/src/components/twui/layout/Divider";
import Stack from "@/src/components/twui/layout/Stack";
import { ArrowLeft } from "lucide-react";
import AddHostFormSection from "./(sections)/add-host-form-section";
import { useContext } from "react";
import { AppContext } from "@/src/pages/__root";
export default function AdminAddHostPage() {
const { pageProps } = useContext(AppContext);
return (
<>
<AdminHero
title="Add New Host"
description="Create a new WireGuard host with a dedicated private subnet"
title={
pageProps?.is_main_host
? "Create Main Host"
: "Add New Host"
}
description={
pageProps?.is_main_host ? (
<span>
Create the main wireguard host <code>(wgui0)</code>{" "}
for this server
</span>
) : (
"Create a new WireGuard host with a dedicated private subnet"
)
}
buttons={
<Button
title="Back to Hosts"
+40
View File
@@ -0,0 +1,40 @@
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<PagePropsType> = async (ctx) => {
const props = ctx.props as PagePropsType;
if (!props.main_host?.name) {
return {
redirect: {
destination: `/admin/hosts/add`,
permanent: false,
},
};
}
const hosts_res = await BunSQLite.select<BUN_SQLITE_WGUI_HOSTS, TableType>({
table: "hosts",
});
const clients_res = await BunSQLite.select<
BUN_SQLITE_WGUI_CLIENTS,
TableType
>({
table: "clients",
});
return {
props: {
hosts: hosts_res.payload || null,
clients: clients_res.payload || null,
},
};
};
export default server;
+6 -90
View File
@@ -3,46 +3,13 @@ import AdminHero from "@/src/components/general/admin-hero";
import Stack from "@/src/components/twui/layout/Stack";
import { SiteData } from "@/src/data/site-data";
import useHostData from "./(hooks)/use-host-data";
import InterfaceConfigSection from "./(sections)/interface-config-section";
import Button from "@/src/components/twui/layout/Button";
import MainHostStatusSection from "./(sections)/main-host-status-section";
import H2 from "@/src/components/twui/layout/H2";
import Divider from "@/src/components/twui/layout/Divider";
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";
import HostPublicIpSection from "./(sections)/host-public-ip-section";
import OtherHostCard from "./(partials)/other-host-card";
import HostCard from "./(partials)/host-card";
export default function AdminHostPage() {
const { hosts, variables, clients } = useHostData();
const other_hosts = (hosts || []).filter((host) => (host.id || 0) != 0);
const main_host_public_ip = variables?.find(
(v) => v.key == "main_host_public_ip_address",
)?.value;
const is_main_host_set = checkIfMainHostIsSet({ variables });
if (!is_main_host_set) {
return (
<>
<AdminHero
title="Hosts"
description="WireGuard server configuration and interface details"
/>
<Stack className="w-full px-6 pb-8 gap-5 items-stretch">
<Span variant="faded">Main Host Not Set up</Span>
<SetupMainHostButton />
</Stack>
</>
);
}
const { hosts, clients } = useHostData();
return (
<>
@@ -51,19 +18,7 @@ export default function AdminHostPage() {
description="WireGuard server configuration and interface details"
buttons={
<>
<Button
title="View Hosts"
variant="outlined"
href="/admin/hosts"
color="gray"
>
View Hosts
</Button>
<Button
title="Add New Host"
variant="outlined"
href="/admin/hosts/add"
>
<Button title="Add New Host" href="/admin/hosts/add">
Add New Host
</Button>
</>
@@ -73,49 +28,10 @@ export default function AdminHostPage() {
<Divider className="mb-6" />
<Stack className="w-full px-6 pb-8 gap-5 items-stretch">
<Row className="w-full justify-between">
<H2>Main Host</H2>
<Row>
<Button
title="View Clients"
size="small"
variant="outlined"
color="primary"
href={`/admin/hosts/0/clients`}
>
View Clients
</Button>
<Button
title="Add Client"
beforeIcon={<Plus />}
size="small"
href={`/admin/hosts/0/clients/add-client`}
>
Add Client
</Button>
</Row>
</Row>
<MainHostStatusSection
variables={variables}
clients={clients}
/>
<HostPublicIpSection
host_id={0}
current_ip={main_host_public_ip || ""}
/>
<InterfaceConfigSection
variables={variables}
clients={clients}
/>
</Stack>
<Divider className="mb-6" />
<Stack className="w-full px-6 pb-8 gap-5 items-stretch">
<H2>Other Hosts</H2>
{other_hosts[0] ? (
{hosts?.[0] ? (
<Stack className="w-full gap-4 items-stretch">
{other_hosts.map((host) => (
<OtherHostCard
{hosts.map((host) => (
<HostCard
key={host.id}
host={host}
client_count={
+21
View File
@@ -0,0 +1,21 @@
import type { PagePropsType } from "@/src/types";
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
const server: BunextPageServerFn<PagePropsType> = async (ctx) => {
const props = ctx.props as PagePropsType;
if (!props.main_host?.name) {
return {
redirect: {
destination: `/admin/hosts/add`,
permanent: false,
},
};
}
return {
props: {},
};
};
export default server;
+1 -2
View File
@@ -8,7 +8,7 @@ import KpiSecondarySection from "./(sections)/kpi-secondary-section";
import PeersTableSection from "./(sections)/peers-table-section";
export default function AdminDashboardPage() {
const { clients, hosts, variables } = useDashboardData();
const { clients, hosts } = useDashboardData();
return (
<>
@@ -21,7 +21,6 @@ export default function AdminDashboardPage() {
<KpiSecondarySection
clients={clients}
hosts={hosts}
variables={variables}
/>
<PeersTableSection clients={clients} />
</Stack>
+23
View File
@@ -0,0 +1,23 @@
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 Stack from "@/src/components/twui/layout/Stack";
export default function AdminWireguardSetupPage() {
return (
<>
<AdminHero
title="Setup"
description="Setup wireguard and wg-ui. Check if dependencies are installed. Update wg-ui and related packages. Etc."
/>
<Divider className="mb-6" />
<Stack className="w-full px-6 pb-8 gap-5 items-stretch"></Stack>
</>
);
}
export const meta: BunextPageModuleMeta = {
title: `Admin Wireguard Setup | ${SiteData["SiteName"]}`,
description: `Setup wireguard and wg-ui. Check if dependencies are installed. Update wg-ui and related packages. Etc.`,
};
+1
View File
@@ -47,6 +47,7 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
short_description: body?.short_description,
listen_port: body?.listen_port,
user,
is_main_host: body.is_main_host || undefined,
});
} catch (error: any) {
return {
+60
View File
@@ -0,0 +1,60 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import userAuth from "@/src/functions/backend/auth/user-auth";
import deleteWireguardHost from "@/src/functions/backend/setup/delete-wireguard-host";
import type { ApiReqParams } from "@/src/types";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
params,
) => {
const req = params.req;
const body = params.body as ApiReqParams;
if (req.method !== "DELETE") {
return {
success: false,
};
}
const { user, user_types } = await userAuth({ req });
if (!user?.logged_in_status) {
return {
success: false,
msg: `Unauthorized`,
logoutUser: true,
};
}
try {
const is_super_admin = checkUserAccess({ user_types });
if (!is_super_admin.success) {
return {
success: false,
msg: `Unauthorized`,
};
}
const host_id = body?.host_id;
if (typeof host_id !== "number" && typeof host_id !== "string") {
throw new Error(`Please pass host ID to be deleted!`);
}
const delete_host_res = await deleteWireguardHost({
host_id: Number(host_id),
user,
});
return delete_host_res;
} catch (error: any) {
return {
success: false,
msg: error.message,
};
}
};
+6 -6
View File
@@ -1,12 +1,11 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import userAuth from "@/src/functions/backend/auth/user-auth";
import setupWireguardHost from "@/src/functions/backend/setup/setup-wireguard-host";
import createWireguardHost from "@/src/functions/backend/setup/create-wireguard-host";
import type { ApiReqParams } from "@/src/types";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
import _ from "lodash";
export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
params,
@@ -40,11 +39,12 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
};
}
return await setupWireguardHost({
host: {
wg_ip_address: body.ip_address,
},
return await createWireguardHost({
wg_ip_address: body.ip_address,
interface: body.interface,
name: "Main",
user,
is_main_host: true,
});
} catch (error: any) {
return {
+4
View File
@@ -67,6 +67,9 @@ export type PagePropsType = {
main_host_dir_names?: ReturnType<typeof grabHostDirnames> | null;
network_interfaces?: string[] | null;
next_listen_port?: number | null;
is_main_host?: boolean | null;
main_host?: BUN_SQLITE_WGUI_HOSTS | null;
next_available_client_ip?: string | null;
};
export type AppContextObject = {
@@ -262,6 +265,7 @@ export type ApiReqParams<
ip_address?: string;
wg_ip_address?: string | null;
is_main_host?: boolean | null;
};
export type UserAuthReturn = {