Apply client changes to the running tunnel without a restart

Saving a client or rule restarted the whole host tunnel (wg-quick down/up)
and re-ran the firewall manager setup, which was slow and briefly dropped
every client on the host.

Client and rule changes now sync peers with `wg syncconf` and re-run the
PostUp script, skipping the firewalld/ufw step. Host creation and edits
still do a full restart.

PostUp now loads the per-host chains with `iptables-restore --noflush`, so
re-running it on a live tunnel swaps the rules atomically instead of
leaving the chains empty between flush and re-add.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-11-03 08:30:41 +01:00
co-authored by Claude Opus 5.5
parent 77ced6cc52
commit 0d8daab3d0
7 changed files with 188 additions and 56 deletions
@@ -0,0 +1,92 @@
import type { APIResponseObject } from "@moduletrace/bunext/types";
import { execFileSync } from "node:child_process";
import grabHostDirnames from "./grab-host-dir-names";
import manageWireguardHost from "./manage-wireguard-host";
type Params = {
host_id: number;
};
function errorOutput({ error }: { error: any }) {
const stderr = error?.stderr ? String(error.stderr).trim() : "";
const stdout = error?.stdout ? String(error.stdout).trim() : "";
return stderr || stdout || error?.message || "unknown error";
}
function isInterfaceUp({ interface_name }: { interface_name: string }) {
try {
execFileSync("ip", ["link", "show", "dev", interface_name], {
stdio: "pipe",
});
return true;
} catch {
return false;
}
}
/**
* Applies an already-written host config and PostUp script to the running
* tunnel without restarting it: peers are synced with `wg syncconf` and the
* per-host iptables chains are reloaded atomically by re-running PostUp.
* Connected clients stay connected. Falls back to bringing the tunnel up if
* it isn't running.
*
* Only for changes that leave the interface itself alone (peers and client
* rules). Address or listen-port changes need a full restart.
*/
export default function applyWireguardHostLive({
host_id,
}: Params): APIResponseObject {
const { HOST_CONFIG_FILE, INTERFACE_NAME, POST_UP_PATH } =
grabHostDirnames({ host: { id: host_id } });
const IS_ROOT =
typeof process.getuid === "function" && process.getuid() === 0;
if (!IS_ROOT) {
return {
success: false,
msg: "wg-ui must run as root to manage wireguard tunnels",
};
}
if (!isInterfaceUp({ interface_name: INTERFACE_NAME })) {
return manageWireguardHost({ host_id, action: "up" });
}
try {
const stripped = execFileSync("wg-quick", ["strip", HOST_CONFIG_FILE], {
encoding: "utf-8",
stdio: "pipe",
});
execFileSync("wg", ["syncconf", INTERFACE_NAME, "/dev/stdin"], {
input: stripped,
stdio: "pipe",
});
} catch (error: any) {
return {
success: false,
msg: `Could not sync peers on ${INTERFACE_NAME}: ${errorOutput({ error })}`,
};
}
try {
execFileSync(POST_UP_PATH, [], {
encoding: "utf-8",
stdio: "pipe",
timeout: 30000,
});
} catch (error: any) {
return {
success: false,
msg: `Peers synced on ${INTERFACE_NAME}, but the firewall rules could not be applied: ${errorOutput({ error })}`,
};
}
return {
success: true,
msg: `Applied changes to ${INTERFACE_NAME} without a restart`,
};
}
@@ -14,9 +14,9 @@ describe("buildHostIptablesScripts", () => {
}); });
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain("iptables -N WGUI0FWD"); expect(res.post_up).toContain(":WGUI0FWD - [0:0]");
expect(res.post_up).toContain("iptables -A WGUI0FWD -j DROP"); expect(res.post_up).toContain("-A WGUI0FWD -j DROP");
expect(res.post_up).toContain("iptables -A WGUI0IN -j DROP"); expect(res.post_up).toContain("-A WGUI0IN -j DROP");
expect(res.post_up).not.toContain("WGUI0FWD -s"); expect(res.post_up).not.toContain("WGUI0FWD -s");
expect(res.post_down).toContain("iptables -X WGUI0FWD"); expect(res.post_down).toContain("iptables -X WGUI0FWD");
expect(res.post_down).toContain( expect(res.post_down).toContain(
@@ -38,10 +38,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.2/32 -j ACCEPT", "-A WGUI0IN -s 10.0.0.2/32 -j ACCEPT",
); );
}); });
@@ -66,10 +66,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT", "-A WGUI0IN -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
); );
}); });
@@ -95,13 +95,13 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT", "-A WGUI0FWD -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT", "-A WGUI0IN -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT",
); );
expect(res.post_up).not.toContain( expect(res.post_up).not.toContain(
"iptables -A WGUI0IN -s 10.5.0.2/32 -d 10.5.0.0/24 -p tcp --dport 443 -j ACCEPT", "-A WGUI0IN -s 10.5.0.2/32 -d 10.5.0.0/24 -p tcp --dport 443 -j ACCEPT",
); );
}); });
@@ -125,16 +125,16 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT", "-A WGUI0IN -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT", "-A WGUI0IN -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT",
); );
}); });
@@ -229,10 +229,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.7/32 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.7/32 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.7/32 -d 10.6.0.0/24 -j ACCEPT", "-A WGUI0IN -s 10.0.0.7/32 -d 10.6.0.0/24 -j ACCEPT",
); );
}); });
@@ -256,10 +256,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT", "-A WGUI0IN -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT",
); );
}); });
@@ -278,10 +278,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.2/32 -d 10.0.0.0/24 -j ACCEPT", "-A WGUI0IN -s 10.0.0.2/32 -d 10.0.0.0/24 -j ACCEPT",
); );
}); });
@@ -307,10 +307,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
const post_up = res.post_up ?? ""; const post_up = res.post_up ?? "";
const sibling_drop = "iptables -A WGUI0FWD -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP"; const sibling_drop = "-A WGUI0FWD -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP";
const sibling_drop_2 = "iptables -A WGUI0FWD -s 10.0.0.2/32 -d 10.2.0.0/24 -j DROP"; const sibling_drop_2 = "-A WGUI0FWD -s 10.0.0.2/32 -d 10.2.0.0/24 -j DROP";
const sibling_input_drop = "iptables -A WGUI0IN -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP"; const sibling_input_drop = "-A WGUI0IN -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP";
const accept = "iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT"; const accept = "-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT";
expect(post_up).toContain(sibling_drop); expect(post_up).toContain(sibling_drop);
expect(post_up).toContain(sibling_drop_2); expect(post_up).toContain(sibling_drop_2);
@@ -337,7 +337,7 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).not.toContain("-d 10.1.0.0/24 -j DROP"); expect(res.post_up).not.toContain("-d 10.1.0.0/24 -j DROP");
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT", "-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
); );
}); });
@@ -356,10 +356,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.7.0.3/32 -j ACCEPT", "-A WGUI0FWD -s 10.7.0.3/32 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.7.0.3/32 -j ACCEPT", "-A WGUI0IN -s 10.7.0.3/32 -j ACCEPT",
); );
expect(res.post_up).not.toContain("-d 10.7.0.0/24"); expect(res.post_up).not.toContain("-d 10.7.0.0/24");
}); });
@@ -382,8 +382,8 @@ describe("buildHostIptablesScripts", () => {
const post_up = res.post_up ?? ""; const post_up = res.post_up ?? "";
const sibling_drop = const sibling_drop =
"iptables -A WGUI0IN -s 10.7.0.4/32 -d 10.3.0.0/24 -j DROP"; "-A WGUI0IN -s 10.7.0.4/32 -d 10.3.0.0/24 -j DROP";
const input_accept = "iptables -A WGUI0IN -s 10.7.0.4/32 -j ACCEPT"; const input_accept = "-A WGUI0IN -s 10.7.0.4/32 -j ACCEPT";
expect(post_up).toContain(sibling_drop); expect(post_up).toContain(sibling_drop);
expect(post_up).toContain(input_accept); expect(post_up).toContain(input_accept);
@@ -414,7 +414,7 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.7.0.5/32 -j ACCEPT", "-A WGUI0FWD -s 10.7.0.5/32 -j ACCEPT",
); );
expect(res.post_up).not.toContain("192.0.2.1"); expect(res.post_up).not.toContain("192.0.2.1");
}); });
@@ -444,9 +444,9 @@ describe("buildHostIptablesScripts", () => {
const post_up = res.post_up ?? ""; const post_up = res.post_up ?? "";
const sibling_drop = const sibling_drop =
"iptables -A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.0/24 -j DROP"; "-A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.0/24 -j DROP";
const accept = const accept =
"iptables -A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.25/32 -p tcp --dport 80 -j ACCEPT"; "-A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.25/32 -p tcp --dport 80 -j ACCEPT";
expect(post_up).toContain(sibling_drop); expect(post_up).toContain(sibling_drop);
expect(post_up).toContain(accept); expect(post_up).toContain(accept);
@@ -464,7 +464,7 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -i wgui0 -p tcp --dport 8443 -j ACCEPT", "-A WGUI0IN -i wgui0 -p tcp --dport 8443 -j ACCEPT",
); );
}); });
@@ -489,4 +489,27 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(false); expect(res.success).toBe(false);
expect(res.msg).toBe("Invalid host id"); expect(res.msg).toBe("Invalid host id");
}); });
test("loads the host chains atomically with iptables-restore", () => {
const res = buildHostIptablesScripts({
...base,
clients: [
{ id: 1, wg_ip_address: "10.0.0.2", allow_all_ips: true },
],
});
const post_up = res.post_up ?? "";
const start = post_up.indexOf(
"iptables-restore --noflush <<'WGUI_RULES' || exit 1",
);
const end = post_up.indexOf("\nWGUI_RULES\n");
const block = post_up.slice(start, end);
expect(start).toBeGreaterThan(-1);
expect(end).toBeGreaterThan(start);
expect(block).toContain("*filter\n:WGUI0FWD - [0:0]\n:WGUI0IN - [0:0]");
expect(block).toContain("-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT");
expect(block.trimEnd().endsWith("-A WGUI0IN -j DROP\nCOMMIT")).toBe(true);
expect(post_up).not.toContain("iptables -F WGUI0FWD");
expect(post_up.indexOf("iptables -I FORWARD 1 -i wgui0 -j WGUI0FWD")).toBeGreaterThan(end);
});
}); });
@@ -119,7 +119,7 @@ function acceptLines({
lines.push( lines.push(
joinArgs([ joinArgs([
`iptables -A ${forward_chain}`, `-A ${forward_chain}`,
`-s ${source}`, `-s ${source}`,
direction, direction,
proto ? `-p ${proto}` : undefined, proto ? `-p ${proto}` : undefined,
@@ -130,7 +130,7 @@ function acceptLines({
lines.push( lines.push(
joinArgs([ joinArgs([
`iptables -A ${input_chain}`, `-A ${input_chain}`,
`-s ${source}`, `-s ${source}`,
input_destination ? `-d ${input_destination}` : undefined, input_destination ? `-d ${input_destination}` : undefined,
proto ? `-p ${proto}` : undefined, proto ? `-p ${proto}` : undefined,
@@ -198,7 +198,7 @@ export default function buildHostIptablesScripts({
} }
accept_lines.push( accept_lines.push(
`iptables -A ${input} -i ${interface_name} -p tcp --dport ${port} -j ACCEPT`, `-A ${input} -i ${interface_name} -p tcp --dport ${port} -j ACCEPT`,
); );
} }
@@ -231,15 +231,15 @@ export default function buildHostIptablesScripts({
for (let s = 0; s < sibling_host_subnets.length; s++) { for (let s = 0; s < sibling_host_subnets.length; s++) {
const sibling_host_subnet = sibling_host_subnets[s]; const sibling_host_subnet = sibling_host_subnets[s];
accept_lines.push( accept_lines.push(
`iptables -A ${forward} -s ${source} -d ${sibling_host_subnet} -j DROP`, `-A ${forward} -s ${source} -d ${sibling_host_subnet} -j DROP`,
`iptables -A ${input} -s ${source} -d ${sibling_host_subnet} -j DROP`, `-A ${input} -s ${source} -d ${sibling_host_subnet} -j DROP`,
); );
} }
if (client.allow_all_ips) { if (client.allow_all_ips) {
accept_lines.push( accept_lines.push(
`iptables -A ${forward} -s ${source} -j ACCEPT`, `-A ${forward} -s ${source} -j ACCEPT`,
`iptables -A ${input} -s ${source} -j ACCEPT`, `-A ${input} -s ${source} -j ACCEPT`,
); );
continue; continue;
@@ -264,12 +264,12 @@ export default function buildHostIptablesScripts({
if (rule.rule_type == "all") { if (rule.rule_type == "all") {
accept_lines.push( accept_lines.push(
joinArgs([ joinArgs([
`iptables -A ${forward}`, `-A ${forward}`,
`-s ${source}`, `-s ${source}`,
`-j ACCEPT`, `-j ACCEPT`,
]), ]),
joinArgs([ joinArgs([
`iptables -A ${input}`, `-A ${input}`,
`-s ${source}`, `-s ${source}`,
scoped_subnet ? `-d ${scoped_subnet}` : undefined, scoped_subnet ? `-d ${scoped_subnet}` : undefined,
`-j ACCEPT`, `-j ACCEPT`,
@@ -314,21 +314,22 @@ export default function buildHostIptablesScripts({
`iptables -D FORWARD -i ${interface_name} -j ACCEPT 2>/dev/null || true`, `iptables -D FORWARD -i ${interface_name} -j ACCEPT 2>/dev/null || true`,
`iptables -D FORWARD -o ${interface_name} -j ACCEPT 2>/dev/null || true`, `iptables -D FORWARD -o ${interface_name} -j ACCEPT 2>/dev/null || true`,
``, ``,
`iptables -N ${forward} 2>/dev/null || true`, // Declaring a chain under --noflush creates or flushes it, and the
`iptables -F ${forward}`, // whole table commits at once — so re-running this on a live tunnel
`iptables -N ${input} 2>/dev/null || true`, // swaps the rules without a window where the chains are empty.
`iptables -F ${input}`, `iptables-restore --noflush <<'WGUI_RULES' || exit 1`,
``, `*filter`,
`iptables -A ${forward} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`, `:${forward} - [0:0]`,
`iptables -A ${input} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`, `:${input} - [0:0]`,
``, `-A ${forward} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
`iptables -A ${input} -i ${interface_name} -p udp --dport 53 -j ACCEPT`, `-A ${input} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
`iptables -A ${input} -i ${interface_name} -p tcp --dport 53 -j ACCEPT`, `-A ${input} -i ${interface_name} -p udp --dport 53 -j ACCEPT`,
``, `-A ${input} -i ${interface_name} -p tcp --dport 53 -j ACCEPT`,
...accept_lines, ...accept_lines,
``, `-A ${forward} -j DROP`,
`iptables -A ${forward} -j DROP`, `-A ${input} -j DROP`,
`iptables -A ${input} -j DROP`, `COMMIT`,
`WGUI_RULES`,
``, ``,
`iptables -C FORWARD -i ${interface_name} -j ${forward} 2>/dev/null || iptables -I FORWARD 1 -i ${interface_name} -j ${forward}`, `iptables -C FORWARD -i ${interface_name} -j ${forward} 2>/dev/null || iptables -I FORWARD 1 -i ${interface_name} -j ${forward}`,
`iptables -C FORWARD -o ${interface_name} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -o ${interface_name} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`, `iptables -C FORWARD -o ${interface_name} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -o ${interface_name} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
@@ -184,6 +184,7 @@ export default async function setupWireguardClient({
host: resolved_host, host: resolved_host,
user, user,
is_update_after_client_setup: true, is_update_after_client_setup: true,
apply_live: true,
}); });
if (!host_setup_res.success) { if (!host_setup_res.success) {
@@ -12,6 +12,7 @@ import BunSQLite from "@moduletrace/bun-sqlite";
import type { TableType, User } from "@/src/types"; import type { TableType, User } from "@/src/types";
import checkPrivateIPAvailability from "./check-private-ip-availability"; import checkPrivateIPAvailability from "./check-private-ip-availability";
import manageWireguardHost from "./manage-wireguard-host"; import manageWireguardHost from "./manage-wireguard-host";
import applyWireguardHostLive from "./apply-wireguard-host-live";
import grabHostDirnames from "./grab-host-dir-names"; import grabHostDirnames from "./grab-host-dir-names";
import grabClientDirnames from "./grab-client-dir-names"; import grabClientDirnames from "./grab-client-dir-names";
import buildHostIptablesScripts from "./build-host-iptables-scripts"; import buildHostIptablesScripts from "./build-host-iptables-scripts";
@@ -27,6 +28,13 @@ type Params = {
wg_subnet_ip?: string; wg_subnet_ip?: string;
user: User; user: User;
is_update_after_client_setup?: boolean; is_update_after_client_setup?: boolean;
/**
* Apply the new config to the running tunnel (`wg syncconf` + atomic
* iptables reload) instead of restarting it, and skip the firewall
* manager rules. Only for peer/rule changes — the interface address and
* listen port must be unchanged.
*/
apply_live?: boolean;
}; };
export default async function setupWireguardHost({ export default async function setupWireguardHost({
@@ -34,6 +42,7 @@ export default async function setupWireguardHost({
wg_subnet_ip, wg_subnet_ip,
user, user,
is_update_after_client_setup, is_update_after_client_setup,
apply_live,
}: Params): Promise<APIResponseObject> { }: Params): Promise<APIResponseObject> {
const { const {
HOST_CLIENTS_DIR, HOST_CLIENTS_DIR,
@@ -296,6 +305,10 @@ export default async function setupWireguardHost({
); );
} }
if (apply_live) {
return applyWireguardHostLive({ host_id: Number(HOST_ID) });
}
const manage_res = manageWireguardHost({ const manage_res = manageWireguardHost({
host_id: HOST_ID, host_id: HOST_ID,
action: "restart", action: "restart",
@@ -125,6 +125,7 @@ export default async function updateWireguardHostPublicIP({
host, host,
user, user,
is_update_after_client_setup: true, is_update_after_client_setup: true,
apply_live: true,
}); });
if (!host_setup_res.success) { if (!host_setup_res.success) {
@@ -116,6 +116,7 @@ export default async function (
host, host,
user, user,
is_update_after_client_setup: true, is_update_after_client_setup: true,
apply_live: true,
}); });
if (!host_setup_res.success) { if (!host_setup_res.success) {