package handlers import ( "encoding/json" "fmt" "net/http" "nano-router/config" "nano-router/dhcp" "nano-router/network" ) func HandleDHCPStatus(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { fail(w, http.StatusMethodNotAllowed, "method not allowed") return } installed := dhcp.IsInstalled() running := false if installed { running = dhcp.ServiceStatus() } ok(w, map[string]interface{}{ "installed": installed, "running": running, }) } type ifaceInfo struct { Name string `json:"name"` IPv4 string `json:"ipv4"` Netmask string `json:"ipv4_mask"` HasGW bool `json:"has_gateway"` } func HandleDHCPConfigGet(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { fail(w, http.StatusMethodNotAllowed, "method not allowed") return } cfg, err := dhcp.Load() if err != nil { fail(w, http.StatusInternalServerError, err.Error()) return } names, _ := network.GetInterfaces() fileCfg, _ := network.ParseConfig() ifaces := []ifaceInfo{} for _, name := range names { if name == "lo" { continue } s, err := network.GetInterfaceStats(name) if err != nil { continue } hasGW := s.Gateway != "" if ncfg, exists := fileCfg[name]; exists && ncfg.Gateway != "" { hasGW = true } ifaces = append(ifaces, ifaceInfo{ Name: name, IPv4: s.IPv4, Netmask: s.IPv4Mask, HasGW: hasGW, }) } ok(w, map[string]interface{}{ "config": cfg, "interfaces": ifaces, }) } func HandleDHCPConfigSave(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { fail(w, http.StatusMethodNotAllowed, "method not allowed") return } var cfg dhcp.Config if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { fail(w, http.StatusBadRequest, "invalid json: "+err.Error()) return } if cfg.Pools == nil { cfg.Pools = []dhcp.Pool{} } for _, pool := range cfg.Pools { if pool.Subnet != "" && pool.Netmask != "" { if msg, overlaps := checkDHCPPoolOverlap(pool.Subnet, pool.Netmask, pool.Interface); overlaps { fail(w, http.StatusConflict, fmt.Sprintf("Пул интерфейса %s: %s", pool.Interface, msg)) return } } } if err := dhcp.Save(&cfg); err != nil { fail(w, http.StatusInternalServerError, err.Error()) return } appCfg, err := config.Load() if err != nil { fail(w, http.StatusInternalServerError, "load config.yaml: "+err.Error()) return } appCfg.DHCP.Enabled = cfg.Enabled appCfg.DHCP.Pools = make([]config.DHCPPool, len(cfg.Pools)) for i, p := range cfg.Pools { appCfg.DHCP.Pools[i] = config.DHCPPool{ Interface: p.Interface, Enabled: p.Enabled, Subnet: p.Subnet, Netmask: p.Netmask, RangeStart: p.RangeStart, RangeEnd: p.RangeEnd, Router: p.Router, DNS: p.DNS, LeaseTime: p.LeaseTime, } } if err := config.Save(appCfg); err != nil { fail(w, http.StatusInternalServerError, "save config.yaml: "+err.Error()) return } ok(w, map[string]string{"message": "saved"}) } func HandleDHCPApply(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { fail(w, http.StatusMethodNotAllowed, "method not allowed") return } if !dhcp.IsInstalled() { fail(w, http.StatusBadRequest, "dnsmasq не установлен — выполните: apk add dnsmasq") return } cfg, err := dhcp.Load() if err != nil { fail(w, http.StatusInternalServerError, err.Error()) return } if err := dhcp.WriteConfigs(cfg); err != nil { fail(w, http.StatusInternalServerError, err.Error()) return } if cfg.Enabled { if err := dhcp.ServiceRestart(); err != nil { fail(w, http.StatusInternalServerError, err.Error()) return } } else { _ = dhcp.ServiceStop() } ok(w, map[string]string{"message": "applied"}) }