feat(wireguard): selektiver .conf-Import — Auswahl einzelner Interfaces per Modal

- GET /wireguard/importable listet alle .conf-Dateien in /etc/wireguard/
  mit already_in-Flag (bereits in DB)
- POST /wireguard/import nimmt optionale Names-Liste; ohne Namen → alles
- edgeguard-ctl wg-import [iface…] importiert nur die genannten Interfaces
- UI: Checkbox-Modal mit "Alle neuen auswählen" + Einzelauswahl;
  bereits importierte Interfaces disabled + grüner Tag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-24 22:09:37 +02:00
parent ac068bc9dd
commit 8d7a43bc8c
10 changed files with 230 additions and 13 deletions

View File

@@ -83,6 +83,8 @@ func (h *WireguardHandler) Register(rg *gin.RouterGroup) {
// row per (iface, peer) with last_handshake + transfer counters.
// Polled by the UI every 10s; no DB write.
g.GET("/status", h.Status)
g.GET("/importable", h.ListImportable)
g.POST("/import", h.Import)
}
// ── Live wg-show status ─────────────────────────────────────────────
@@ -142,6 +144,45 @@ func (h *WireguardHandler) Status(c *gin.Context) {
response.OK(c, gin.H{"status": rows})
}
// ── Import ────────────────────────────────────────────────────────
const wgImportDir = "/etc/wireguard"
// ListImportable scans /etc/wireguard for *.conf files and reports
// which are new (not yet in the DB) vs. already imported.
func (h *WireguardHandler) ListImportable(c *gin.Context) {
im := wireguard.NewImporter(h.Ifaces, h.Peers, h.Box)
entries, err := im.ListImportable(c.Request.Context(), wgImportDir)
if err != nil {
response.Internal(c, err)
return
}
response.OK(c, gin.H{"entries": entries})
}
// Import imports selected (or all) WireGuard .conf files from
// /etc/wireguard into the DB. Body: { "names": ["wg0","wg1"] }.
// Empty or missing names → import all.
func (h *WireguardHandler) Import(c *gin.Context) {
var req struct {
Names []string `json:"names"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err)
return
}
im := wireguard.NewImporter(h.Ifaces, h.Peers, h.Box)
res, err := im.ImportSelected(c.Request.Context(), wgImportDir, req.Names)
if err != nil {
response.Internal(c, err)
return
}
_ = h.Audit.Log(c.Request.Context(), actorOf(c), "wireguard.import",
fmt.Sprintf("%d ifaces", res.IfacesAdded), res, h.NodeID)
h.reload(c.Request.Context(), "import")
response.OK(c, res)
}
// ── Keygen ────────────────────────────────────────────────────────
func (h *WireguardHandler) GenerateKeypair(c *gin.Context) {