Files
nixos-router/pkgs/router-webui/templates/apiref.html
T
nilsandClaude Sonnet 5.5 64e08fb485 Add typed settings areas to the REST API
/api/v1/settings/router/firewall, .../router/lans/<name>, .../system, down to
a single option: GET reads, PUT replaces, PATCH merges, DELETE removes, with
the settings' ETag/If-Match and the same limits as the whole-document edit
(editSettings is now shared). Values are checked against a JSON Schema made
from the option documentation (schema.go reads the types, which nixosOptionsDoc
describes in words); a wrong type, a value out of range or an unknown key is
refused with its place. The OpenAPI document has the area paths with their
schemas; the API reference page lists every area's options.

Tests: unit tests for the type reader, the validator and the paths; the real
option file and a router's own settings validate (ROUTER_OPTIONS_JSON,
LIVE_SETTINGS_JSON); a subtest in apply.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
2026-11-01 20:09:20 +01:00

65 lines
3.8 KiB
HTML

{{define "content"}}
{{with .Data}}
<section class="panel">
<h2>Using the API</h2>
<p>Everything the MCP server offers, over plain HTTP. Base address <code>{{.Base}}</code>; the machine-readable description
is <a href="/api/openapi.json">/api/openapi.json</a> (OpenAPI 3.1, no token needed).</p>
<p>Every call needs a token on the <a href="/tokens">API access</a> page, sent as <code>Authorization: Bearer rtr_…</code>.
A <strong>read</strong> token may use the read operations; a <strong>change</strong> token also the others. Changes to the
settings are saved, never applied: you review them under <a href="/changes">Changes</a> and apply them yourself.</p>
<p>Errors are JSON, <code>{"error": "…"}</code>, with the status 400 (refused or a bad argument), 401 (no or an expired token),
403 (the token's access is too low), 409 (stale ETag) or 500. Results are JSON, except the pending changes and the
journal, which are plain text.{{if .HasOptions}} <code>GET /api/v1/options</code> lists every router option with its type,
default and description: what <code>settings</code> may contain.{{end}}</p>
<p class="hint">Examples use <code>-k</code> because the certificate is self-signed unless you set up a
<a href="/certificate">certificate</a>; put your token in <code>$TOKEN</code>.</p>
</section>
<div class="cols">
{{range .Ops}}
<section class="panel">
<h2><code>{{.Method}} {{.Path}}</code> <span class="muted">{{.Summary}}</span></h2>
<p class="hint">Access: {{.Scope}}</p>
<p>{{.Description}}</p>
{{if .Params}}
<div class="scroll"><table>
<tr><th>Parameter</th><th>Type</th><th></th><th>Description</th></tr>
{{range .Params}}<tr><td><code>{{.Name}}</code></td><td>{{.Type}}</td><td>{{if .Required}}required{{end}}</td><td>{{.Description}}</td></tr>{{end}}
</table></div>
{{end}}
{{with .Body}}<p><strong>Body:</strong> {{.}}</p>{{end}}
<pre class="log">{{.Curl}}</pre>
</section>
{{end}}
</div>
<section class="panel">
<h2>Settings areas</h2>
<p>The settings are a tree, and every part of it has its own path below <code>/api/v1/settings</code>:
<code>router/firewall</code>, <code>router/lans/lan</code> (one LAN, by its name), <code>router/dns/hosts</code>,
<code>system</code>, down to a single option like <code>router/wan/interface</code> (lists are replaced whole; their
elements have no paths). The OpenAPI document describes each with the type of its value.</p>
<ul>
<li><code>GET</code> reads the part (404 when it is not set and the defaults apply); the <code>ETag</code> header is the
settings' version.</li>
<li><code>PUT</code> replaces it, <code>PATCH</code> merges a JSON merge patch into it (objects only), <code>DELETE</code> removes
it. They need <code>If-Match</code> with the ETag, need a change token, and are refused (400, with the place) when a value
does not fit its option's type or a key is not an option.</li>
<li>Like every edit through the API they are saved, never applied; <code>GET /api/v1/settings/check</code> evaluates them.</li>
</ul>
<pre class="log">curl -sk -H "Authorization: Bearer $TOKEN" https://{{.Host}}/api/v1/settings/router/dns
curl -sk -X PUT -H "Authorization: Bearer $TOKEN" -H 'If-Match: "ETAG"' \
-d '{"domain":"home.example.org","dynamicLeases":true}' https://{{.Host}}/api/v1/settings/router/dns</pre>
</section>
{{range .Areas}}
<section class="panel">
<h2><code>{{.Path}}</code></h2>
<div class="scroll"><table class="opts">
<tr><th>Option</th><th>Type</th><th>Default</th><th>Description</th></tr>
{{range .Options}}<tr><td><code>{{.Name}}</code></td><td>{{.Type}}</td><td>{{with .Default}}<code>{{.}}</code>{{end}}</td><td>{{.Description}}</td></tr>{{end}}
</table></div>
<p class="hint"><code>&lt;name&gt;</code> is the key of an entry in a map, <code>*</code> an element of a list.</p>
</section>
{{end}}
{{end}}
{{end}}