8ad4c3576d
- 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
285 行
7.1 KiB
Go
285 行
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"})
|
|
}
|