From bbee7e04ed42d922f38da33f64c11d392376056c Mon Sep 17 00:00:00 2001
From: Bernd Storath <32197462+kaaax0815@users.noreply.github.com>
Date: Fri, 14 Mar 2025 12:19:26 +0100
Subject: [PATCH] Feat: add ability to restart interface (#1740)

add ability to restart interface
---
 docker-compose.dev.yml                        |  2 +-
 .../Admin/RestartInterfaceDialog.vue          | 24 +++++++++++++++++
 src/app/pages/admin/interface.vue             | 27 +++++++++++++++++++
 src/i18n/locales/en.json                      |  7 ++++-
 .../api/admin/interface/restart.post.ts       |  5 ++++
 src/server/utils/WireGuard.ts                 |  5 ++++
 src/server/utils/wgHelper.ts                  |  4 +++
 7 files changed, 72 insertions(+), 2 deletions(-)
 create mode 100644 src/app/components/Admin/RestartInterfaceDialog.vue
 create mode 100644 src/server/api/admin/interface/restart.post.ts

diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 9b191b34..8f9dad6e 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -16,7 +16,7 @@ services:
       - NET_ADMIN
       - SYS_MODULE
     environment:
-      - INIT_ENABLED=false
+      - INIT_ENABLED=true
       - INIT_HOST=test
       - INIT_PORT=51820
       - INIT_USERNAME=testtest
diff --git a/src/app/components/Admin/RestartInterfaceDialog.vue b/src/app/components/Admin/RestartInterfaceDialog.vue
new file mode 100644
index 00000000..e6c92473
--- /dev/null
+++ b/src/app/components/Admin/RestartInterfaceDialog.vue
@@ -0,0 +1,24 @@
+<template>
+  <BaseDialog :trigger-class="triggerClass">
+    <template #trigger><slot /></template>
+    <template #title>{{ $t('admin.interface.restart') }}</template>
+    <template #description>
+      {{ $t('admin.interface.restartWarn') }}
+    </template>
+    <template #actions>
+      <DialogClose as-child>
+        <BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
+      </DialogClose>
+      <DialogClose as-child>
+        <BaseButton @click="$emit('restart')">
+          {{ $t('admin.interface.restart') }}
+        </BaseButton>
+      </DialogClose>
+    </template>
+  </BaseDialog>
+</template>
+
+<script lang="ts" setup>
+defineEmits(['restart']);
+defineProps<{ triggerClass?: string }>();
+</script>
diff --git a/src/app/pages/admin/interface.vue b/src/app/pages/admin/interface.vue
index 63309c45..18988943 100644
--- a/src/app/pages/admin/interface.vue
+++ b/src/app/pages/admin/interface.vue
@@ -34,8 +34,19 @@
           <FormActionField
             :label="$t('admin.interface.changeCidr')"
             class="w-full"
+            tabindex="-1"
           />
         </AdminCidrDialog>
+        <AdminRestartInterfaceDialog
+          trigger-class="col-span-2"
+          @restart="restartInterface"
+        >
+          <FormActionField
+            :label="$t('admin.interface.restart')"
+            class="w-full"
+            tabindex="-1"
+          />
+        </AdminRestartInterfaceDialog>
       </FormGroup>
     </FormElement>
   </main>
@@ -82,4 +93,20 @@ const _changeCidr = useSubmit(
 async function changeCidr(ipv4Cidr: string, ipv6Cidr: string) {
   await _changeCidr({ ipv4Cidr, ipv6Cidr });
 }
+
+const _restartInterface = useSubmit(
+  `/api/admin/interface/restart`,
+  {
+    method: 'post',
+  },
+  {
+    revert,
+    successMsg: t('admin.interface.restartSuccess'),
+    errorMsg: t('admin.interface.restartError'),
+  }
+);
+
+async function restartInterface() {
+  await _restartInterface(undefined);
+}
 </script>
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index 71b10925..f429034f 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -159,7 +159,12 @@
       "deviceDesc": "Ethernet device the wireguard traffic should be forwarded through",
       "mtuDesc": "MTU WireGuard will use",
       "portDesc": "UDP Port WireGuard will listen on (you probably want to change Config Port too)",
-      "changeCidr": "Change CIDR"
+      "changeCidr": "Change CIDR",
+      "restart": "Restart Interface",
+      "restartDesc": "Restart the WireGuard interface",
+      "restartWarn": "Are you sure to restart the interface? This will disconnect all clients.",
+      "restartSuccess": "Interface restarted",
+      "restartError": "Failed to restart interface"
     },
     "introText": "Welcome to the admin panel.\n\nHere you can manage the general settings, the configuration, the interface settings and the hooks.\n\nStart by choosing one of the sections in the sidebar."
   },
diff --git a/src/server/api/admin/interface/restart.post.ts b/src/server/api/admin/interface/restart.post.ts
new file mode 100644
index 00000000..aaf7ae39
--- /dev/null
+++ b/src/server/api/admin/interface/restart.post.ts
@@ -0,0 +1,5 @@
+export default definePermissionEventHandler('admin', 'any', async () => {
+  await WireGuard.Restart();
+
+  return { success: true };
+});
diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts
index 1fa931b0..a5a16aca 100644
--- a/src/server/utils/WireGuard.ts
+++ b/src/server/utils/WireGuard.ts
@@ -193,6 +193,11 @@ class WireGuard {
     await wg.down(wgInterface.name).catch(() => {});
   }
 
+  async Restart() {
+    const wgInterface = await Database.interfaces.get();
+    await wg.restart(wgInterface.name);
+  }
+
   async cronJob() {
     const clients = await Database.clients.getAll();
     // Expires Feature
diff --git a/src/server/utils/wgHelper.ts b/src/server/utils/wgHelper.ts
index 170a140f..ab79109d 100644
--- a/src/server/utils/wgHelper.ts
+++ b/src/server/utils/wgHelper.ts
@@ -92,6 +92,10 @@ Endpoint = ${userConfig.host}:${userConfig.port}`;
     return exec(`wg-quick down ${infName}`);
   },
 
+  restart: (infName: string) => {
+    return exec(`wg-quick down ${infName}; wg-quick up ${infName}`);
+  },
+
   sync: (infName: string) => {
     return exec(`wg syncconf ${infName} <(wg-quick strip ${infName})`);
   },