14.04.2026 Update

This commit is contained in:
2026-04-15 11:38:26 +03:00
parent 6aa0349f5d
commit f50d79fab3
45 changed files with 5645 additions and 751 deletions

View File

@@ -5,8 +5,8 @@ import (
"net/http"
"strings"
"alpine-router/config"
"alpine-router/network"
"nano-router/config"
"nano-router/network"
)
type apiResp struct {
@@ -47,10 +47,16 @@ func HandleInterfaces(w http.ResponseWriter, r *http.Request) {
*network.InterfaceStats
Pending bool `json:"pending"`
Label string `json:"label,omitempty"`
NAT bool `json:"nat"`
}
appCfg, _ := config.Load()
var natIfaces []string
if appCfg != nil {
natIfaces = appCfg.NAT.Interfaces
}
result := make([]iface, 0, len(names))
existingNames := map[string]bool{}
for _, name := range names {
@@ -64,15 +70,30 @@ func HandleInterfaces(w http.ResponseWriter, r *http.Request) {
}
hasPending := network.GetPendingConfig(name) != nil
label := ""
ifaceType := ""
if appCfg != nil && appCfg.Interfaces != nil {
if ic, ok := appCfg.Interfaces[name]; ok {
label = ic.Label
ifaceType = ic.Type
}
}
result = append(result, iface{s, hasPending, label})
s.Type = ifaceType
if s.Type == "" && s.Gateway != "" {
s.Type = "wan"
}
if s.Type == "" && s.Mode != "loopback" {
s.Type = "lan"
}
isNAT := false
for _, ni := range natIfaces {
if ni == name {
isNAT = true
break
}
}
result = append(result, iface{s, hasPending, label, isNAT})
}
// Also include pending VLAN configs not yet present in the system.
for name, cfg := range network.GetAllPending() {
if existingNames[name] || !network.IsVLAN(name) {
continue
@@ -81,6 +102,7 @@ func HandleInterfaces(w http.ResponseWriter, r *http.Request) {
Name: name,
State: "unknown",
Mode: cfg.Mode,
Type: cfg.Type,
IPv6: []string{},
}
if cfg.Mode == "static" {
@@ -88,13 +110,26 @@ func HandleInterfaces(w http.ResponseWriter, r *http.Request) {
s.IPv4Mask = cfg.Netmask
s.Gateway = cfg.Gateway
}
if s.Type == "" && s.Gateway != "" {
s.Type = "wan"
}
if s.Type == "" {
s.Type = "lan"
}
label := cfg.Label
if label == "" && appCfg != nil && appCfg.Interfaces != nil {
if ic, ok := appCfg.Interfaces[name]; ok {
label = ic.Label
}
}
result = append(result, iface{s, true, label})
isNAT := false
for _, ni := range natIfaces {
if ni == name {
isNAT = true
break
}
}
result = append(result, iface{s, true, label, isNAT})
}
ok(w, result)
@@ -131,9 +166,9 @@ func HandleInterfaceAction(w http.ResponseWriter, r *http.Request) {
var err error
switch action {
case "up":
err = network.IfUp(name)
err = network.LinkUp(name)
case "down":
err = network.IfDown(name)
err = network.LinkDown(name)
case "restart":
err = network.IfRestart(name)
case "delete":
@@ -166,16 +201,21 @@ func HandleConfig(w http.ResponseWriter, r *http.Request) {
case http.MethodGet:
appCfg, _ := config.Load()
label := ""
ifaceType := ""
if appCfg != nil && appCfg.Interfaces != nil {
if ic, ok2 := appCfg.Interfaces[name]; ok2 {
label = ic.Label
ifaceType = ic.Type
}
}
if cfg := network.GetPendingConfig(name); cfg != nil {
if cfg.Label != "" {
label = cfg.Label
}
ok(w, map[string]interface{}{"config": cfg, "pending": true, "label": label})
if cfg.Type != "" {
ifaceType = cfg.Type
}
ok(w, map[string]interface{}{"config": cfg, "pending": true, "label": label, "type": ifaceType})
return
}
fileCfg, err := network.ParseConfig()
@@ -184,12 +224,17 @@ func HandleConfig(w http.ResponseWriter, r *http.Request) {
return
}
if cfg, exists := fileCfg[name]; exists {
ok(w, map[string]interface{}{"config": cfg, "pending": false, "label": label})
if ifaceType == "" {
ifaceType = cfg.Type
}
ok(w, map[string]interface{}{"config": cfg, "pending": false, "label": label, "type": ifaceType})
} else {
defaultType := "lan"
ok(w, map[string]interface{}{
"config": &network.InterfaceConfig{Name: name, Auto: true, Mode: "dhcp", Extra: map[string]string{}},
"config": &network.InterfaceConfig{Name: name, Auto: true, Mode: "dhcp", Type: defaultType, Extra: map[string]string{}},
"pending": false,
"label": label,
"type": defaultType,
})
}
@@ -200,6 +245,49 @@ func HandleConfig(w http.ResponseWriter, r *http.Request) {
return
}
cfg.Name = name
if cfg.Type != "wan" && cfg.Type != "lan" {
fail(w, http.StatusBadRequest, "type must be 'wan' or 'lan'")
return
}
if network.IsVLAN(name) && cfg.Type != "lan" {
fail(w, http.StatusBadRequest, "VLAN interface must be type 'lan'")
return
}
if cfg.Type == "lan" {
if cfg.Mode == "dhcp" {
fail(w, http.StatusBadRequest, "LAN interface cannot use DHCP mode")
return
}
if cfg.Gateway != "" {
fail(w, http.StatusBadRequest, "LAN interface cannot have a gateway")
return
}
if len(cfg.DNS) > 0 {
fail(w, http.StatusBadRequest, "LAN interface cannot have DNS servers")
return
}
}
if cfg.Type == "wan" && cfg.Mode == "static" && cfg.Address == "" {
fail(w, http.StatusBadRequest, "WAN interface in static mode requires an IP address")
return
}
if network.IsVLAN(name) {
parent := network.VLANParent(name)
appCfgCheck, _ := config.Load()
if appCfgCheck != nil && appCfgCheck.Interfaces != nil {
if pic, ok := appCfgCheck.Interfaces[parent]; ok && pic.Type == "wan" {
fail(w, http.StatusBadRequest, "VLAN cannot be created on a WAN interface ("+parent+")")
return
}
}
}
if msg, overlaps := checkInterfaceOverlap(&cfg); overlaps {
fail(w, http.StatusConflict, msg)
return
}
if cfg.Extra == nil {
cfg.Extra = map[string]string{}
}
@@ -215,6 +303,7 @@ func HandleConfig(w http.ResponseWriter, r *http.Request) {
}
appCfg.Interfaces[name] = &config.InterfaceConfig{
Label: cfg.Label,
Type: cfg.Type,
Auto: cfg.Auto,
Mode: cfg.Mode,
Address: cfg.Address,