Add public IP field and Network interface to host form

This commit is contained in:
2026-09-20 12:22:54 +01:00
parent 957f2de13c
commit 8b40a876a1
17 changed files with 327 additions and 21 deletions
@@ -1,4 +1,4 @@
import type { BUN_SQLITE_WGUI_HOSTS } from "@/db/types/db";
import type { BUN_SQLITE_WGUI_HOSTS, BUN_SQLITE_WGUI_VARIABLES } from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import type { TableType, User } from "@/src/types";
@@ -7,6 +7,8 @@ import grabHostPublicIPAddress from "./grab-host-public-ip-address";
type Params = {
wg_ip_address?: string | null;
public_ip_address?: string | null;
interface?: string | null;
user: User;
};
@@ -20,6 +22,8 @@ type Params = {
*/
export default async function createWireguardHost({
wg_ip_address,
public_ip_address,
interface: iface,
user,
}: Params): Promise<APIResponseObject> {
const wg_ip = (wg_ip_address || "").trim();
@@ -31,7 +35,9 @@ export default async function createWireguardHost({
};
}
const public_ip_address = await grabHostPublicIPAddress();
const detected_public_ip = await grabHostPublicIPAddress();
const final_public_ip = public_ip_address?.trim() || detected_public_ip;
const final_interface = iface?.trim() || undefined;
try {
const insert_host = await BunSQLite.insert<
@@ -43,7 +49,8 @@ export default async function createWireguardHost({
{
user_id: user.id,
wg_ip_address: wg_ip,
public_ip_address: public_ip_address || undefined,
public_ip_address: final_public_ip || undefined,
interface: final_interface,
},
],
});
@@ -61,9 +68,26 @@ export default async function createWireguardHost({
id: host_id,
user_id: user.id,
wg_ip_address: wg_ip,
public_ip_address: public_ip_address || undefined,
public_ip_address: final_public_ip || undefined,
interface: final_interface,
};
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,
@@ -93,4 +117,4 @@ export default async function createWireguardHost({
msg: error.message,
};
}
}
}
@@ -0,0 +1,85 @@
import { execSync } from "node:child_process";
import os from "node:os";
const VIRTUAL_INTERFACE_PATTERNS = [
/^wg\d+$/,
/^docker\d*$/,
/^br\d*$/,
/^veth.*/,
/^tun.*/,
/^tap.*/,
/^vlan.*/,
/^bond\d*$/,
/^macvlan.*/,
/^ipvlan.*/,
/^wgui\d+$/,
/^virbr\d*$/,
/^vboxnet\d*$/,
/^vmnet\d*$/,
];
function isVirtualInterface(name: string): boolean {
return VIRTUAL_INTERFACE_PATTERNS.some((pattern) =>
pattern.test(name),
);
}
function isPhysicalInterface(name: string): boolean {
try {
const devicePath = `/sys/class/net/${name}/device`;
const result = execSync(`test -e ${devicePath} && echo yes || echo no`, {
encoding: "utf-8",
}).trim();
return result === "yes";
} catch {
return true;
}
}
/**
* Function to grab all available physical network interfaces on the machine
* @returns Array of interface names (e.g. ["enp2s0", "wlan0"])
*/
export default async function grabHostNetworkInterfaces(): Promise<string[]> {
try {
const interfaces = os.networkInterfaces();
const names: string[] = [];
for (const [name, addrs] of Object.entries(interfaces)) {
if (!addrs) continue;
if (name === "lo") continue;
if (isVirtualInterface(name)) continue;
for (const addr of addrs) {
if (addr.family === "IPv4" && !addr.internal) {
if (isPhysicalInterface(name)) {
names.push(name);
}
break;
}
}
}
if (names.length > 0) {
return names.sort();
}
} catch (error) {}
try {
const route = execSync(`ip -4 addr show`, { encoding: "utf-8" })
.trim()
.split(/\n/);
const names: string[] = [];
for (const line of route) {
const match = line.match(/^\d+:\s+(\S+):/);
if (match && match[1] && match[1] !== "lo") {
const iface = match[1];
if (!isVirtualInterface(iface) && isPhysicalInterface(iface)) {
names.push(iface);
}
}
}
if (names.length > 0) return names.sort();
} catch (error) {}
return [`eth0`];
}
@@ -100,7 +100,7 @@ export default async function setupWireguardHost({
rules_by_client_id.set(rule.client_id, existing_rules);
}
const TARGET_INTERFACE = await grabHostNetworkInterface();
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 ||
@@ -158,21 +158,29 @@ export default async function setupWireguardHost({
}).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: [
{
key: "main_host_wg_ip_address",
value: HOST_WG_IP,
},
{
key: "main_host_wg_public_key",
value: HOST_PUBLIC_KEY,
},
],
data: variables_data as BUN_SQLITE_WGUI_VARIABLES[],
update_on_duplicate: true,
});
@@ -43,6 +43,10 @@ export default async function syncWireguardHosts() {
(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) {
throw new Error(`Main Host not set yet`);
}
@@ -52,6 +56,7 @@ export default async function syncWireguardHosts() {
id: 0,
public_ip_address: main_host_public_ip,
wg_ip_address: main_host_ip,
interface: main_host_interface,
},
...(hosts.payload || []),
];