Add the appliance web UI look, interface names, MCP and REST API, local zone
Web UI: dark/light themes (appliance layout, sidebar, switch checkboxes), theme preference on the Advanced page, Changes page with the pending diff. Interface names: router.interfaces.<name>.mac, lans.<l>.deviceName and pppoe.vlanName; a rename is staged for the next reboot by router-apply. Remote access: an MCP server (POST /mcp) and an OpenAPI REST API (/api/v1, /api/openapi.json) with the same tokens, scopes, limits and audit log; tools to read status, settings, leases, logs (get_logs), and to edit and check the saved settings (never applied). Tokens on the API access page. Firewall: a reserved `local` zone (the router itself) as the destination of rules, so the web UI or SSH can be allowed from the WAN. PLAN.md lists what is still left. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
@@ -26,6 +26,8 @@ type Prefs struct {
|
||||
// VLANs defined in the network page ("enp2s0.1001") that may not be used
|
||||
// yet; the interface dropdowns offer them.
|
||||
VLANs []string `json:"vlans,omitempty"`
|
||||
// Theme is "light" or "dark"; empty follows the browser's setting.
|
||||
Theme string `json:"theme,omitempty"`
|
||||
}
|
||||
|
||||
var defaultPrefs = Prefs{PingTargets: []string{"gateway", "1.1.1.1", "9.9.9.9"}}
|
||||
@@ -400,18 +402,35 @@ func (a *app) dashboardFragment(w http.ResponseWriter, r *http.Request, sess ses
|
||||
}
|
||||
}
|
||||
|
||||
// savePrefs takes the Advanced page's two small forms: the ping targets, or
|
||||
// the theme.
|
||||
func (a *app) savePrefs(w http.ResponseWriter, r *http.Request, sess session) {
|
||||
targets, err := parsePingTargets(r.PostFormValue("pingTargets"))
|
||||
_ = r.ParseForm()
|
||||
prefs := a.sys.loadPrefs()
|
||||
what := "Ping targets"
|
||||
var err error
|
||||
if r.PostForm.Has("theme") {
|
||||
what = "Appearance"
|
||||
switch t := r.PostFormValue("theme"); t {
|
||||
case "", "light", "dark":
|
||||
prefs.Theme = t
|
||||
default:
|
||||
err = fmt.Errorf("unknown theme %q", t)
|
||||
}
|
||||
} else {
|
||||
var targets []string
|
||||
if targets, err = parsePingTargets(r.PostFormValue("pingTargets")); err == nil {
|
||||
prefs.PingTargets = targets
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
prefs := a.sys.loadPrefs()
|
||||
prefs.PingTargets = targets
|
||||
err = a.sys.savePrefs(prefs)
|
||||
}
|
||||
if err != nil {
|
||||
http.Redirect(w, r, "/advanced?msg="+url.QueryEscape("Ping targets not saved: "+err.Error()), http.StatusSeeOther)
|
||||
http.Redirect(w, r, "/advanced?msg="+url.QueryEscape(what+" not saved: "+err.Error()), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/advanced?msg="+url.QueryEscape("Ping targets saved."), http.StatusSeeOther)
|
||||
http.Redirect(w, r, "/advanced?msg="+url.QueryEscape(what+" saved."), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// backup streams the state directory (settings, secrets, certificate, metric
|
||||
|
||||
Reference in New Issue
Block a user