Add wg-quick/systemctl service control and bulk-delete list views

Per-server Start/Stop/Restart via systemd wg-quick@<iface>.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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvrfUytqd74H6WcQkRzFM4
This commit is contained in:
sysops
2026-07-25 00:19:13 +02:00
co-authored by Claude Sonnet 5
parent 3a89a3cb5c
commit c29edfdcc3
6 changed files with 860 additions and 1 deletions
+84
View File
@@ -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@<iface>.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@<iface>.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@<iface>.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@<iface>.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 {