Files
dhcp-dns-manager/internal/web/config_handler.go
T
CNBUGS AI 8ad4c3576d Fix DHCP client unable to get IP and config not persisting
- Fixed verifyAssignment being too strict for new clients
- Fixed parseRequestedIP string conversion bug
- Fixed response sent to 0.0.0.0 instead of broadcast address
- Added SO_BROADCAST support for UDP socket
- Fixed session persistence after page refresh (localStorage)
- Added in-memory session store for auth middleware
- Added config reloader so DHCP server picks up web UI changes dynamically
2026-04-24 16:03:54 +08:00

285 line
7.1 KiB
Go

package web
import (
"dhcp-dns-manager/internal/config"
"encoding/json"
"net/http"
"sync"
"github.com/gin-gonic/gin"
)
// ConfigManager 配置管理器
type ConfigManager struct {
configPath string
config *config.Config
mu sync.RWMutex
}
// NewConfigManager 创建配置管理器
func NewConfigManager(path string) (*ConfigManager, error) {
cfg, err := config.LoadConfig(path)
if err != nil {
return nil, err
}
return &ConfigManager{
configPath: path,
config: cfg,
}, nil
}
// GetConfig 获取配置
func (cm *ConfigManager) GetConfig() *config.Config {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.config
}
// SaveConfig 保存配置
func (cm *ConfigManager) SaveConfig(cfg *config.Config) error {
cm.mu.Lock()
defer cm.mu.Unlock()
cm.config = cfg
return cfg.Save(cm.configPath)
}
// UpdateDHCPConfig 更新 DHCP 配置(支持部分更新)
func (cm *ConfigManager) UpdateDHCPConfig(updates map[string]interface{}) error {
cm.mu.Lock()
defer cm.mu.Unlock()
// 将更新合并到现有配置
for key, value := range updates {
switch key {
case "enabled":
if v, ok := value.(bool); ok {
cm.config.DHCP.Enabled = v
}
case "interface":
if v, ok := value.(string); ok {
cm.config.DHCP.Interface = v
}
case "network":
if v, ok := value.(string); ok {
cm.config.DHCP.Network = v
}
case "netmask":
if v, ok := value.(string); ok {
cm.config.DHCP.Netmask = v
}
case "gateway":
if v, ok := value.(string); ok {
cm.config.DHCP.Gateway = v
}
case "dns_servers":
if v, ok := value.([]interface{}); ok {
servers := make([]string, len(v))
for i, s := range v {
servers[i] = s.(string)
}
cm.config.DHCP.DNSServers = servers
}
case "lease_time":
if v, ok := value.(float64); ok {
cm.config.DHCP.LeaseTime = int(v)
}
case "ip_pool_start":
if v, ok := value.(string); ok {
cm.config.DHCP.IPPoolStart = v
}
case "ip_pool_end":
if v, ok := value.(string); ok {
cm.config.DHCP.IPPoolEnd = v
}
case "domain_name":
if v, ok := value.(string); ok {
cm.config.DHCP.DomainName = v
}
case "ntp_servers":
if v, ok := value.([]interface{}); ok {
servers := make([]string, len(v))
for i, s := range v {
servers[i] = s.(string)
}
cm.config.DHCP.NTPServers = servers
}
case "broadcast_address":
if v, ok := value.(string); ok {
cm.config.DHCP.BroadcastAddress = v
}
case "excluded_ips":
if v, ok := value.([]interface{}); ok {
ips := make([]string, len(v))
for i, ip := range v {
ips[i] = ip.(string)
}
cm.config.DHCP.ExcludedIPs = ips
}
}
}
return cm.config.Save(cm.configPath)
}
// UpdateDNSConfig 更新 DNS 配置(支持部分更新)
func (cm *ConfigManager) UpdateDNSConfig(updates map[string]interface{}) error {
cm.mu.Lock()
defer cm.mu.Unlock()
for key, value := range updates {
switch key {
case "enabled":
if v, ok := value.(bool); ok {
cm.config.DNS.Enabled = v
}
case "listen_addr":
if v, ok := value.(string); ok {
cm.config.DNS.ListenAddr = v
}
case "listen_port":
if v, ok := value.(float64); ok {
cm.config.DNS.ListenPort = int(v)
}
case "upstream":
if v, ok := value.([]interface{}); ok {
servers := make([]string, len(v))
for i, s := range v {
servers[i] = s.(string)
}
cm.config.DNS.Upstream = servers
}
case "cache_size":
if v, ok := value.(float64); ok {
cm.config.DNS.CacheSize = int(v)
}
case "cache_ttl":
if v, ok := value.(float64); ok {
cm.config.DNS.CacheTTL = int(v)
}
case "recursion":
if v, ok := value.(bool); ok {
cm.config.DNS.Recursion = v
}
}
}
return cm.config.Save(cm.configPath)
}
// handleGetDHCPConfig 获取 DHCP 配置
func (s *Server) handleGetDHCPConfig(c *gin.Context) {
cm := c.MustGet("configManager").(*ConfigManager)
cfg := cm.GetConfig()
c.JSON(http.StatusOK, gin.H{"config": cfg.DHCP})
}
// handleUpdateDHCPConfig 更新 DHCP 配置
func (s *Server) handleUpdateDHCPConfig(c *gin.Context) {
cm := c.MustGet("configManager").(*ConfigManager)
var updates map[string]interface{}
if err := c.ShouldBindJSON(&updates); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON: " + err.Error()})
return
}
// 验证必填字段
if network, ok := updates["network"]; ok && network.(string) == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "network is required"})
return
}
if err := cm.UpdateDHCPConfig(updates); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Save failed: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "DHCP config updated"})
}
// handleGetDNSConfig 获取 DNS 配置
func (s *Server) handleGetDNSConfig(c *gin.Context) {
cm := c.MustGet("configManager").(*ConfigManager)
cfg := cm.GetConfig()
c.JSON(http.StatusOK, gin.H{"config": cfg.DNS})
}
// handleUpdateDNSConfig 更新 DNS 配置
func (s *Server) handleUpdateDNSConfig(c *gin.Context) {
cm := c.MustGet("configManager").(*ConfigManager)
var updates map[string]interface{}
if err := c.ShouldBindJSON(&updates); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON: " + err.Error()})
return
}
// 验证端口
if port, ok := updates["listen_port"]; ok {
if port.(float64) < 1 || port.(float64) > 65535 {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid port"})
return
}
}
if err := cm.UpdateDNSConfig(updates); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Save failed: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "DNS config updated"})
}
// handleGetFullConfig 获取完整配置
func (s *Server) handleGetFullConfig(c *gin.Context) {
cm := c.MustGet("configManager").(*ConfigManager)
cfg := cm.GetConfig()
c.JSON(http.StatusOK, gin.H{
"dhcp": cfg.DHCP,
"dns": cfg.DNS,
"web": cfg.Web,
})
}
// handleRestartService 重启服务
func (s *Server) handleRestartService(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Restart requested. Please restart the service manually: sudo systemctl restart dhcp-dns-manager",
})
}
// ExportConfig 导出配置
func (s *Server) handleExportConfig(c *gin.Context) {
cm := c.MustGet("configManager").(*ConfigManager)
cfg := cm.GetConfig()
c.Header("Content-Type", "application/json")
c.Header("Content-Disposition", "attachment; filename=dhcp-dns-config.json")
c.JSON(http.StatusOK, cfg)
}
// ImportConfig 导入配置
func (s *Server) handleImportConfig(c *gin.Context) {
file, _, err := c.Request.FormFile("config")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to upload config file"})
return
}
defer file.Close()
var cfg config.Config
if err := json.NewDecoder(file).Decode(&cfg); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid config file: " + err.Error()})
return
}
cm := c.MustGet("configManager").(*ConfigManager)
if err := cm.SaveConfig(&cfg); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Config imported successfully"})
}