Add client eviction feature

- Add EvictClient method to DHCP Server
- Add /api/dhcp/leases/evict endpoint to force client IP release
- Add 'Evict' button in Web UI for online clients
- Update table layout to include Action column
- Evicted client will be forced to get a new IP on next DHCP request
This commit is contained in:
CNBUGS AI
2026-04-24 16:59:14 +08:00
parent 9d0bae5d0c
commit 5bb84c159a
4 changed files with 83 additions and 1 deletions
+23
View File
@@ -700,3 +700,26 @@ func newBroadcastUDPConn(host string, port int) (*net.UDPConn, error) {
return udpConn, nil
}
// EvictClient removes a client's lease, forcing them to get a new IP
func (s *Server) EvictClient(mac string) error {
s.leaseMutex.Lock()
defer s.leaseMutex.Unlock()
lease, exists := s.leases[mac]
if !exists {
return fmt.Errorf("lease not found for MAC %s", mac)
}
// Remove from in-memory lease map
delete(s.leases, mac)
delete(s.usedIPs, lease.IP)
// Delete from database
if err := s.db.Where("mac = ?", mac).Delete(&db.DHCPLease{}).Error; err != nil {
return fmt.Errorf("failed to delete lease from database: %v", err)
}
log.Printf("DHCP: Evicted client %s (%s)", mac, lease.IP)
return nil
}
+25
View File
@@ -117,6 +117,7 @@ func (s *Server) setupRoutes() {
protected.GET("/dhcp/bindings", s.handleGetBindings)
protected.POST("/dhcp/bindings", s.handleCreateBinding)
protected.DELETE("/dhcp/bindings/:id", s.handleDeleteBinding)
protected.POST("/dhcp/leases/evict", s.handleEvictClient)
// DNS
protected.GET("/dns/config", s.handleGetDNSConfig)
@@ -282,6 +283,30 @@ func (s *Server) handleDeleteBinding(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Binding deleted"})
}
func (s *Server) handleEvictClient(c *gin.Context) {
var req struct {
MAC string `json:"mac"`
IP string `json:"ip"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if req.MAC == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "MAC is required"})
return
}
if err := s.dhcpServer.EvictClient(req.MAC); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Client evicted successfully"})
}
func (s *Server) handleGetRecords(c *gin.Context) {
records, err := s.dnsServer.GetDNSRecords()
if err != nil {