2026-04-13 09:46:02 +03:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
2026-04-15 11:38:26 +03:00
|
|
|
"fmt"
|
2026-04-13 09:46:02 +03:00
|
|
|
"net/http"
|
|
|
|
|
|
2026-04-15 11:38:26 +03:00
|
|
|
"nano-router/config"
|
|
|
|
|
"nano-router/dhcp"
|
|
|
|
|
"nano-router/network"
|
2026-04-13 09:46:02 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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{}
|
|
|
|
|
}
|
2026-04-15 11:38:26 +03:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-13 09:46:02 +03:00
|
|
|
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"})
|
|
|
|
|
}
|