Reverse zone suggestions: yours and public, dismiss, per-view switch; child zones take their records

Suggestions split into networks that are yours (private IPv4 /24, ULA /48, an IPv6 /48 this server has an address in), which Create all creates, and public addresses (/32 or /128) to create only if they are yours or dismiss. A view can turn suggestions off, which also keeps it out of choosing a reverse zone's view (database version 13). A zone created inside another in the same view takes the parent's records under it; a delegation and its glue stay, a linked parent keeps them with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-12 22:01:18 +01:00
1 parent aa4b004a14
commit b24c730b97
21 files changed
+543 -107

No files matched your search

+8 -5
View File
@@ -31,15 +31,15 @@ func loadState(ctx context.Context, e execer) (*model.State, error) {
}
}
rows, err := e.QueryContext(ctx, `SELECT id, name, position, match_clients, match_destinations, match_recursive_only, comment, exposure, acknowledged, version, created_at, updated_at, created_by, updated_by FROM views`)
rows, err := e.QueryContext(ctx, `SELECT id, name, position, match_clients, match_destinations, match_recursive_only, comment, exposure, acknowledged, reverse_suggestions_off, dismissed_reverse, version, created_at, updated_at, created_by, updated_by FROM views`)
if err != nil {
return nil, err
}
for rows.Next() {
v := &model.View{}
var mc, md, acks, ca, ua string
var mc, md, acks, dismissed, ca, ua string
var mro sql.NullInt64
if err := rows.Scan(&v.ID, &v.Name, &v.Position, &mc, &md, &mro, &v.Comment, &v.Exposure, &acks, &v.Version, &ca, &ua, &v.CreatedBy, &v.UpdatedBy); err != nil {
if err := rows.Scan(&v.ID, &v.Name, &v.Position, &mc, &md, &mro, &v.Comment, &v.Exposure, &acks, &v.ReverseSuggestionsOff, &dismissed, &v.Version, &ca, &ua, &v.CreatedBy, &v.UpdatedBy); err != nil {
rows.Close()
return nil, err
}
@@ -48,6 +48,9 @@ func loadState(ctx context.Context, e execer) (*model.State, error) {
if acks != "[]" && acks != "" {
json.Unmarshal([]byte(acks), &v.Acknowledged)
}
if dismissed != "[]" && dismissed != "" {
json.Unmarshal([]byte(dismissed), &v.DismissedReverse)
}
if mro.Valid {
v.MatchRecursiveOnly = model.Ptr(mro.Int64 != 0)
}
@@ -267,8 +270,8 @@ func saveState(ctx context.Context, e execer, old, new *model.State) error {
if v.MatchRecursiveOnly != nil {
mro = boolInt(*v.MatchRecursiveOnly)
}
_, err := e.ExecContext(ctx, `INSERT OR REPLACE INTO views (id, name, position, match_clients, match_destinations, match_recursive_only, comment, exposure, acknowledged, version, created_at, updated_at, created_by, updated_by) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
v.ID, v.Name, v.Position, jsonText(nonNil(v.MatchClients)), jsonText(nonNil(v.MatchDestinations)), mro, v.Comment, v.Exposure, jsonText(nonNilAcks(v.Acknowledged)), v.Version, ts(v.CreatedAt), ts(v.UpdatedAt), v.CreatedBy, v.UpdatedBy)
_, err := e.ExecContext(ctx, `INSERT OR REPLACE INTO views (id, name, position, match_clients, match_destinations, match_recursive_only, comment, exposure, acknowledged, reverse_suggestions_off, dismissed_reverse, version, created_at, updated_at, created_by, updated_by) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
v.ID, v.Name, v.Position, jsonText(nonNil(v.MatchClients)), jsonText(nonNil(v.MatchDestinations)), mro, v.Comment, v.Exposure, jsonText(nonNilAcks(v.Acknowledged)), v.ReverseSuggestionsOff, jsonText(nonNil(v.DismissedReverse)), v.Version, ts(v.CreatedAt), ts(v.UpdatedAt), v.CreatedBy, v.UpdatedBy)
return err
}, func(id int64) error { return del(ctx, e, "views", id) }); err != nil {
return err