Add token expiry, edit notice, log allowlist, local-zone test; merge PLAN.md into SPECS.md
get_logs needs a unit from an allowlist (router-*, kea, unbound, networkd, nftables, ddclient, acme, sshd, kernel). API tokens are valid for 1, 7, 30 (default) or 90 days, or without limit. The overview and the Changes page say which token made the unapplied changes. options.json (router://options) no longer holds the options' source paths: they differ between builds of a release, so a router rebuilding itself offline had to build the file and failed (found by the update test). Web UI: panels flow into columns on wide screens and the width is no longer capped; on phones the navigation is a menu button; the Advanced page uses panels; VLAN dropdowns show their chosen names; the duplicate WireGuard heading is gone. basic: a subtest for wan -> local rules. SPECS.md is now the only document (decisions, how each part works, and what is not done yet); PLAN.md is gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{{define "content"}}
|
||||
<section>
|
||||
<section class="panel">
|
||||
<h2>Settings</h2>
|
||||
<p>The complete settings: the <code>router</code> options of nixos-router as JSON, plus <code>system</code>.
|
||||
They are checked when applied; nothing changes until then.</p>
|
||||
@@ -10,7 +10,9 @@
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="cols">
|
||||
|
||||
<section class="panel">
|
||||
<h2>Appearance</h2>
|
||||
<form method="post" action="/prefs" class="narrow">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}">
|
||||
@@ -26,7 +28,7 @@
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section class="panel">
|
||||
<h2>Dashboard</h2>
|
||||
<form method="post" action="/prefs" class="narrow">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}">
|
||||
@@ -37,7 +39,7 @@
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section class="panel">
|
||||
<h2>Password</h2>
|
||||
<p>One password for this web UI and for root on the console and over SSH.</p>
|
||||
<form method="post" action="/password" class="narrow">
|
||||
@@ -49,17 +51,18 @@
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section class="panel">
|
||||
<h2>Reboot</h2>
|
||||
<p>Restarts the router; routing stops for a minute or two. A new kernel from an update only runs after this.</p>
|
||||
<form method="post" action="/reboot" data-confirm="Reboot the router now?"><input type="hidden" name="csrf" value="{{.CSRF}}"><button>Reboot</button></form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section class="panel">
|
||||
<h2>Backup</h2>
|
||||
<p>Everything on <code>/var/lib/router</code>: settings, PPPoE password, WireGuard keys, passwords, the web UI's
|
||||
certificate and the dashboard history. Keep the file safe. To restore, put it on a USB stick and run
|
||||
<code>router-setup</code> from the installer: it offers to install from it.</p>
|
||||
<form method="post" action="/backup"><input type="hidden" name="csrf" value="{{.CSRF}}"><button>Download backup</button></form>
|
||||
</section>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{define "content"}}
|
||||
<div class="cols">
|
||||
{{with .Data}}
|
||||
{{$e := .Errors}}{{$f := .Form}}
|
||||
<section>
|
||||
@@ -42,4 +43,5 @@
|
||||
{{else}}<p class="muted">Nothing yet. After you save and apply, the router orders the certificate (a minute or two); renewals follow 30 days before it expires and need no restart.</p>{{end}}
|
||||
</section>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
{{if .Diff}}
|
||||
<p>The saved settings differ from the confirmed (running) ones: <code>-</code> lines go away, <code>+</code> lines are new.
|
||||
Nothing is applied until you press Apply.</p>
|
||||
{{with .TokenEdit}}<p class="error">{{.}}</p>{{end}}
|
||||
<pre class="log">{{.Diff}}</pre>
|
||||
<div class="actions">
|
||||
<form method="post" action="/apply"><input type="hidden" name="csrf" value="{{$.CSRF}}"><button>Apply</button></form>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{define "content"}}
|
||||
<div class="cols">
|
||||
{{with .Data}}
|
||||
{{$e := .Errors}}{{$f := .Form}}
|
||||
<p class="hint">Keeps DNS records at your provider pointing at the router's internet addresses: it checks regularly and
|
||||
@@ -41,4 +42,5 @@
|
||||
{{else}}<p class="muted">Nothing yet. Once applied, the log of ddclient shows up here.</p>{{end}}
|
||||
</section>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
<header>
|
||||
<strong>router</strong>
|
||||
{{if .LoggedIn}}
|
||||
<input type="checkbox" id="menu" class="menu-toggle" aria-label="Menu">
|
||||
<label for="menu" class="menu-button">Menu</label>
|
||||
<nav>
|
||||
<a href="/"{{if eq .Title "Overview"}} aria-current="page"{{end}}>Overview</a>
|
||||
<div class="group"><span>Network</span>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<legend>Uplink (WAN)</legend>
|
||||
<label>Interface
|
||||
<select name="wan.interface">{{$wan := .Form.WAN.Interface}}
|
||||
{{range choices $.Data.Devices $wan}}<option value="{{.}}" {{if eq . $wan}}selected{{end}}>{{.}}</option>{{end}}
|
||||
{{range choices $.Data.Devices $wan}}<option value="{{.}}" {{if eq . $wan}}selected{{end}}>{{devLabel $.Data.Devices .}}</option>{{end}}
|
||||
</select>
|
||||
</label>
|
||||
{{with index $e "wan.interface"}}<span class="field-error">{{.}}</span>{{end}}
|
||||
@@ -95,7 +95,7 @@
|
||||
<td><input name="lan.{{$i}}.name" value="{{$l.Name}}" size="8" placeholder="new"></td>
|
||||
<td><select name="lan.{{$i}}.interface">{{$cur := $l.Interface}}
|
||||
{{if not $cur}}<option value="" selected></option>{{end}}
|
||||
{{range choices $.Data.Devices $cur}}<option value="{{.}}" {{if eq . $cur}}selected{{end}}>{{.}}</option>{{end}}
|
||||
{{range choices $.Data.Devices $cur}}<option value="{{.}}" {{if eq . $cur}}selected{{end}}>{{devLabel $.Data.Devices .}}</option>{{end}}
|
||||
</select></td>
|
||||
<td><input name="lan.{{$i}}.address" value="{{$l.Address}}" size="13"></td>
|
||||
<td><input name="lan.{{$i}}.prefix" value="{{$l.Prefix}}" size="2" inputmode="numeric" placeholder="24"></td>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{define "content"}}
|
||||
<div class="cols">
|
||||
{{$s := .Data.Status}}
|
||||
{{if $s.Applying}}
|
||||
<section class="busy"><h2>Applying…</h2><p>Building the new configuration. This page refreshes on its own.</p></section>
|
||||
@@ -14,6 +15,7 @@
|
||||
{{else if $s.Unapplied}}
|
||||
<section class="pending">
|
||||
<p>The settings have changes that are not part of the confirmed system. <a href="/changes">Review the changes</a>.</p>
|
||||
{{with $s.TokenEdit}}<p><strong>{{.}}</strong></p>{{end}}
|
||||
<div class="actions">
|
||||
<form method="post" action="/apply"><input type="hidden" name="csrf" value="{{.CSRF}}"><button>Apply</button></form>
|
||||
<form method="post" action="/discard"><input type="hidden" name="csrf" value="{{.CSRF}}"><button class="secondary">Discard changes</button></form>
|
||||
@@ -44,4 +46,5 @@
|
||||
</section>
|
||||
{{end}}
|
||||
<script src="/static/dashboard.js" defer></script>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
{{if .Tokens}}
|
||||
<div class="scroll">
|
||||
<table>
|
||||
<tr><th>Name</th><th>Access</th><th>Created</th><th>Last used</th><th></th></tr>
|
||||
<tr><th>Name</th><th>Access</th><th>Created</th><th>Expires</th><th>Last used</th><th></th></tr>
|
||||
{{range .Tokens}}<tr><td>{{.Name}}</td><td>{{.Scope}}</td><td>{{.Created.Format "2006-01-02"}}</td>
|
||||
<td>{{if .Expires.IsZero}}<span class="hint">never</span>{{else if .Expired}}<span class="bad">expired {{.Expires.Format "2006-01-02"}}</span>{{else}}{{.Expires.Format "2006-01-02"}}{{end}}</td>
|
||||
<td>{{if .LastUsed.IsZero}}<span class="hint">never</span>{{else}}{{.LastUsed.Format "2006-01-02 15:04"}}{{end}}</td>
|
||||
<td><form method="post" action="/tokens/revoke" data-confirm="Revoke this token?"><input type="hidden" name="csrf" value="{{$.CSRF}}"><input type="hidden" name="id" value="{{.ID}}"><button class="link">revoke</button></form></td></tr>{{end}}
|
||||
</table>
|
||||
@@ -43,6 +44,15 @@
|
||||
<option value="change">change: also edit the settings</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Valid for
|
||||
<select name="valid">
|
||||
<option value="1d">1 day</option>
|
||||
<option value="7d">7 days</option>
|
||||
<option value="30d" selected>30 days</option>
|
||||
<option value="90d">90 days</option>
|
||||
<option value="never">no limit</option>
|
||||
</select>
|
||||
</label>
|
||||
<button>Create token</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{define "content"}}
|
||||
<div class="cols">
|
||||
{{$d := .Data}}
|
||||
<section>
|
||||
<h2>Installed</h2>
|
||||
@@ -60,4 +61,5 @@
|
||||
<pre class="log">{{$d.Status.Log}}</pre>
|
||||
</section>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -132,7 +132,6 @@
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>New tunnel</h2>
|
||||
{{template "tunnelform" (tform .New $.CSRF true)}}
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user