Web UI: zone naming and reverse zones, ACL suggestions, record form
- New zone dialog: choose Domain or Reverse zone. Reverse zones are created from a network and their name is generated; both kinds show the exact zone name and flag likely mistakes (addresses or URLs as names, host-like names, duplicates in the view, parent/child zones, networks off the octet/nibble boundaries) before anything is submitted. - Record form: clears every field but the type after an add; shows the record exactly as it will be stored and warns about unqualified names. In reverse zones it takes an address, defaults to PTR, suggests host names of A/AAAA records with that address and warns about a second PTR; the record list shows addresses. - Address match lists (options, view match lists, ACL elements) offer the existing ACLs, TSIG keys and built-ins as one-click suggestions and flag unknown names. - Server: in reverse zones a record can be named by its address, and a PTR target like nas.example.com without the final dot is read as the full host name (a bare label is refused). Warnings noticed while parsing record data reach the result again. - Fixes: brand link hover underline, the dashboard's "New zone" link opening the dialog twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3b7bee67f8
commit
bf7f99874b
19 files changed
+577
-35
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { h, mount, table, field, input, select, textarea, toast, errorBox, confirmDialog, modal, lines, checkbox } from "../ui.js";
|
||||
import { amlContext, suggestions } from "../aml.js";
|
||||
import { get, change, describeResult } from "../api.js";
|
||||
import { rerender } from "../app.js";
|
||||
import { optionsEditor } from "./options.js";
|
||||
@@ -44,6 +45,7 @@ export async function render(el, params, query) {
|
||||
|
||||
async function viewDialog(v, viewless) {
|
||||
const err = h("div");
|
||||
const ctx = await amlContext();
|
||||
const name = input({ value: v ? v.name : "", placeholder: "internal" });
|
||||
const mc = textarea(v ? (v.match_clients || []).join("\n") : "", { class: "mono", rows: 4, placeholder: "192.168.1.0/24\nlocalhost\n(empty = any)" });
|
||||
const md = textarea(v ? (v.match_destinations || []).join("\n") : "", { class: "mono", rows: 2, placeholder: "(empty = any)" });
|
||||
@@ -52,8 +54,8 @@ async function viewDialog(v, viewless) {
|
||||
const adopt = checkbox("Move the " + viewless.length + " zone(s) that are not in a view into this view", true);
|
||||
const body = h("div", null, h("div", { class: "form-grid" },
|
||||
field("Name", name),
|
||||
field("Match clients", mc, "One element per line: addresses, prefixes, ACL names, key NAME, any/none/localhost/localnets; !negates. First match wins."),
|
||||
field("Match destinations", md, "Server addresses this view answers on (optional)."),
|
||||
field("Match clients", h("div", null, mc, suggestions(mc, ctx)), "One element per line: addresses, prefixes, ACL names, key NAME, any/none/localhost/localnets; !negates. First match wins."),
|
||||
field("Match destinations", h("div", null, md, suggestions(md, ctx)), "Server addresses this view answers on (optional)."),
|
||||
field("Recursive only", mro, "If yes, only recursive queries match this view."),
|
||||
field("Comment", comment)),
|
||||
!v && viewless.length ? h("div", { class: "msg warn" }, "Once views exist, named requires every zone to be inside a view. ", adopt) : null,
|
||||
|
||||
Reference in new issue
Block a user