From c29edfdcc38563b3acbeaac2e61e861131523e1f Mon Sep 17 00:00:00 2001 From: sysops Date: Sat, 25 Jul 2026 00:19:13 +0200 Subject: [PATCH] Add wg-quick/systemctl service control and bulk-delete list views Per-server Start/Stop/Restart via systemd wg-quick@.service units plus live status badge, and a table/list-view toggle with checkbox bulk-delete for both the server list and per-server client list. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01PvrfUytqd74H6WcQkRzFM4 --- DEVLOG.md | 237 +++++++++++++++++++++++++++++ handler/routes.go | 84 +++++++++++ main.go | 4 + templates/server_clients.html | 174 ++++++++++++++++++++- templates/servers.html | 274 ++++++++++++++++++++++++++++++++++ wireguard/service.go | 88 +++++++++++ 6 files changed, 860 insertions(+), 1 deletion(-) create mode 100644 wireguard/service.go diff --git a/DEVLOG.md b/DEVLOG.md index 7975624..c0cbabf 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -2202,3 +2202,240 @@ Keine Commits in dieser Session. - DEVLOG.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ --- +## 2026-07-12 23:39 – 23:39 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +- 067d0af Fix "no such file" error creating clients on non-default servers + +### Geänderte Dateien +- DEVLOG.md | 33 +++++++++++++++++++++++++++++++++ +- handler/routes.go | 6 +++++- +- templates/server_clients.html | 2 +- + +--- +## 2026-07-12 23:40 – 23:42 (2m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +- 0dbb916 Fix nil-pointer crash generating QR codes for non-default servers + +### Geänderte Dateien +- store/jsondb/jsondb.go | 56 ++++++++++++++++++++++++++++++++++++-------------------- +- util/util.go | 10 +++++++++- + +--- +## 2026-07-12 23:43 – 23:43 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- store/jsondb/jsondb.go | 56 ++++++++++++++++++++++++++++++++++++-------------------- +- util/util.go | 10 +++++++++- + +--- +## 2026-07-12 23:44 – 23:45 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- store/jsondb/jsondb.go | 56 ++++++++++++++++++++++++++++++++++++-------------------- +- util/util.go | 10 +++++++++- + +--- +## 2026-07-23 23:31 – 23:31 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- store/jsondb/jsondb.go | 56 ++++++++++++++++++++++++++++++++++++-------------------- +- util/util.go | 10 +++++++++- + +--- +## 2026-07-23 23:33 – 23:47 (13m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- store/jsondb/jsondb.go | 56 ++++++++++++++++++++++++++++++++++++-------------------- +- util/util.go | 10 +++++++++- + +--- +## 2026-07-24 00:03 – 00:04 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- store/jsondb/jsondb.go | 56 ++++++++++++++++++++++++++++++++++++-------------------- +- util/util.go | 10 +++++++++- + +--- +## 2026-07-24 00:04 – 00:05 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +- 388a837 Add per-server Interface/KeyPair edit hardening and OPNsense config import + +### Geänderte Dateien +- handler/routes.go | 23 ++++++++-- +- handler/routes_opnsense_import.go | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +- main.go | 2 + +- model/client.go | 40 ++++++++-------- +- model/server.go | 2 +- +- model/setting.go | 9 +++- +- opnsense/map.go | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +- opnsense/parse.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ +- templates/servers.html | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- +- util/util.go | 10 +++- + +--- +## 2026-07-24 00:13 – 00:14 (1m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +- 3a89a3c Fix OPNsense config.xml root element mismatch + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-24 00:18 – 00:21 (2m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-24 00:22 – 00:22 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-24 08:59 – 09:00 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 ++++++++++++++++++++++++++++++++----------------------- + +--- +## 2026-07-24 23:55 – 23:55 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 ++++++++++++++++++++++++++++++++----------------------- + +--- +## 2026-07-24 23:58 – 00:04 (5m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-25 00:05 – 00:05 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-25 00:05 – 00:06 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-25 00:08 – 00:11 (2m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-25 00:11 – 00:11 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-25 00:15 – 00:15 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- +## 2026-07-25 00:17 – 00:18 (1m) +**Beschreibung:** Claude Code Session +**Projekt:** wireguard-ui-multi + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- opnsense/parse.go | 78 +++++++++++++++++++++++++++++++++++++++++++++--------------------------------- + +--- diff --git a/handler/routes.go b/handler/routes.go index 9c4a52f..bbc7bf2 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -32,6 +32,7 @@ import ( "github.com/ngoduykhanh/wireguard-ui/system" "github.com/ngoduykhanh/wireguard-ui/telegram" "github.com/ngoduykhanh/wireguard-ui/util" + "github.com/ngoduykhanh/wireguard-ui/wireguard" ) var usernameRegexp = regexp.MustCompile("^\\w[\\w\\-.]*$") @@ -896,6 +897,89 @@ func ApplyFirewallHandler(db store.IStore) echo.HandlerFunc { } } +// ServerServiceStatus reports whether a server's WireGuard interface is +// currently active/enabled at the systemd level (wg-quick@.service). +func ServerServiceStatus(db store.IStore) echo.HandlerFunc { + return func(c echo.Context) error { + serverID := c.Param("id") + server, err := db.GetServerByID(serverID) + if err != nil { + return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Server not found"}) + } + + active, enabled, err := wireguard.Status(c.Request().Context(), server.Interface.Name) + if err != nil { + log.Errorf("Failed to get service status for server %s: %v", serverID, err) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "active": active, + "enabled": enabled, + }) + } +} + +// ServerServiceStart brings up a server's WireGuard interface via +// `systemctl start wg-quick@.service`. Admin-only. +func ServerServiceStart(db store.IStore) echo.HandlerFunc { + return func(c echo.Context) error { + serverID := c.Param("id") + server, err := db.GetServerByID(serverID) + if err != nil { + return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Server not found"}) + } + + if err := wireguard.Start(c.Request().Context(), server.Interface.Name); err != nil { + log.Errorf("Failed to start service for server %s: %v", serverID, err) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) + } + log.Infof("Started wireguard interface for server %s", serverID) + + return c.JSON(http.StatusOK, map[string]interface{}{"active": true}) + } +} + +// ServerServiceStop brings down a server's WireGuard interface via +// `systemctl stop wg-quick@.service`. Admin-only. +func ServerServiceStop(db store.IStore) echo.HandlerFunc { + return func(c echo.Context) error { + serverID := c.Param("id") + server, err := db.GetServerByID(serverID) + if err != nil { + return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Server not found"}) + } + + if err := wireguard.Stop(c.Request().Context(), server.Interface.Name); err != nil { + log.Errorf("Failed to stop service for server %s: %v", serverID, err) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) + } + log.Infof("Stopped wireguard interface for server %s", serverID) + + return c.JSON(http.StatusOK, map[string]interface{}{"active": false}) + } +} + +// ServerServiceRestart restarts a server's WireGuard interface via +// `systemctl restart wg-quick@.service`. Admin-only. +func ServerServiceRestart(db store.IStore) echo.HandlerFunc { + return func(c echo.Context) error { + serverID := c.Param("id") + server, err := db.GetServerByID(serverID) + if err != nil { + return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Server not found"}) + } + + if err := wireguard.Restart(c.Request().Context(), server.Interface.Name); err != nil { + log.Errorf("Failed to restart service for server %s: %v", serverID, err) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) + } + log.Infof("Restarted wireguard interface for server %s", serverID) + + return c.JSON(http.StatusOK, map[string]interface{}{"active": true}) + } +} + // NewClient handler func NewClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { diff --git a/main.go b/main.go index 2533652..a92e0b6 100644 --- a/main.go +++ b/main.go @@ -288,6 +288,10 @@ func main() { app.POST(util.BasePath+"/servers/:id/firewall/rules/:ruleId", handler.UpdateFirewallRuleHandler(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.POST(util.BasePath+"/servers/:id/firewall/rules/:ruleId/delete", handler.DeleteFirewallRuleHandler(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.POST(util.BasePath+"/servers/:id/firewall/apply", handler.ApplyFirewallHandler(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) + app.GET(util.BasePath+"/servers/:id/service-status", handler.ServerServiceStatus(db), handler.ValidSession, handler.RequireServerAccess(db)) + app.POST(util.BasePath+"/servers/:id/service/start", handler.ServerServiceStart(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) + app.POST(util.BasePath+"/servers/:id/service/stop", handler.ServerServiceStop(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) + app.POST(util.BasePath+"/servers/:id/service/restart", handler.ServerServiceRestart(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.POST(util.BasePath+"/backup/download", handler.DownloadBackup(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession) app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession) diff --git a/templates/server_clients.html b/templates/server_clients.html index cdf0206..3da44f7 100644 --- a/templates/server_clients.html +++ b/templates/server_clients.html @@ -23,10 +23,45 @@ Wireguard Clients {{define "page_content"}}
-
Clients — {{.server.Name}} ({{.serverID}})
+
+
Clients — {{.server.Name}} ({{.serverID}})
+
+ + +
+
+
@@ -260,6 +295,11 @@ Wireguard Clients {{define "bottom_js"}}