Files
vibedns/web/templates/partials.html
T
owenandClaude Sonnet 5 4895c8fd1e Add a customizable HTTP/HTTPS block page for sinkholed queries
Serves a page explaining why a domain was blocked instead of leaving a
sinkholed client with a dead connection. Binds its own HTTP/HTTPS
listeners with self-signed, per-hostname TLS certs generated on the
fly, re-evaluates the requesting client against the policy engine per
request, and renders an HTML template editable from Settings with a
live preview.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTKpGMQzpfDsvedu1hvSUf
2026-08-17 00:24:00 -05:00

186 lines
8.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{/* Shared building blocks used across pages. */}}
{{define "sidebar"}}
<nav class="sidebar-nav" aria-label="Main navigation">
<a class="sidebar-link {{if eq .Nav "dashboard"}}active{{end}}" href="/">
<i class="bi bi-speedometer2"></i><span>Dashboard</span>
</a>
<div class="sidebar-heading">DNS</div>
<a class="sidebar-link {{if eq .Nav "zones"}}active{{end}}" href="/zones">
<i class="bi bi-diagram-3"></i><span>Forward Zones</span>
</a>
<a class="sidebar-link {{if eq .Nav "zones-reverse"}}active{{end}}" href="/zones/reverse">
<i class="bi bi-arrow-left-right"></i><span>Reverse Zones</span>
</a>
<a class="sidebar-link {{if eq .Nav "records"}}active{{end}}" href="/records">
<i class="bi bi-list-columns-reverse"></i><span>Records</span>
</a>
<a class="sidebar-link {{if eq .Nav "resolver"}}active{{end}}" href="/resolver">
<i class="bi bi-globe2"></i><span>Resolver</span>
</a>
<a class="sidebar-link {{if eq .Nav "cache"}}active{{end}}" href="/cache">
<i class="bi bi-lightning-charge"></i><span>Cache</span>
</a>
<div class="sidebar-heading">Policies</div>
<a class="sidebar-link {{if eq .Nav "networks"}}active{{end}}" href="/policies/networks">
<i class="bi bi-router"></i><span>Client Networks</span>
</a>
<a class="sidebar-link {{if eq .Nav "blacklists"}}active{{end}}" href="/policies/blacklists">
<i class="bi bi-shield-slash"></i><span>Blacklists</span>
</a>
<a class="sidebar-link {{if eq .Nav "allowlists"}}active{{end}}" href="/policies/allowlists">
<i class="bi bi-shield-check"></i><span>Allowlists</span>
</a>
<a class="sidebar-link {{if eq .Nav "policies"}}active{{end}}" href="/policies">
<i class="bi bi-sliders"></i><span>Rules</span>
</a>
<div class="sidebar-heading">Operations</div>
<a class="sidebar-link {{if eq .Nav "querylog"}}active{{end}}" href="/querylog">
<i class="bi bi-journal-text"></i><span>Query Log</span>
</a>
<a class="sidebar-link {{if eq .Nav "audit"}}active{{end}}" href="/audit">
<i class="bi bi-clipboard-check"></i><span>Audit Log</span>
</a>
<a class="sidebar-link {{if eq .Nav "tools"}}active{{end}}" href="/tools">
<i class="bi bi-tools"></i><span>Tools</span>
</a>
<div class="sidebar-heading">Settings</div>
<a class="sidebar-link {{if eq .Nav "settings"}}active{{end}}" href="/settings/dns">
<i class="bi bi-gear"></i><span>Settings</span>
</a>
<a class="sidebar-link {{if eq .Nav "account"}}active{{end}}" href="/account">
<i class="bi bi-person-badge"></i><span>Account</span>
</a>
</nav>
{{end}}
{{/* Page header: title, optional subtitle and a slot for actions. */}}
{{define "pageheader"}}
<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-4">
<div>
<h1 class="page-title">{{.Title}}</h1>
{{if .Subtitle}}<p class="text-body-secondary mb-0">{{.Subtitle}}</p>{{end}}
</div>
{{if .Actions}}<div class="d-flex flex-wrap gap-2">{{.Actions}}</div>{{end}}
</div>
{{end}}
{{/* A statistic tile. Pass: Label, Value, Icon, Sub, Tone. */}}
{{define "stat"}}
<div class="col-6 col-md-4 col-xl-3">
<div class="card stat-card h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="stat-label">{{.Label}}</div>
{{if .Icon}}<i class="bi {{.Icon}} stat-icon {{with .Tone}}text-{{.}}{{end}}"></i>{{end}}
</div>
<div class="stat-value">{{.Value}}</div>
{{if .Sub}}<div class="stat-sub">{{.Sub}}</div>{{end}}
</div>
</div>
</div>
{{end}}
{{/* Empty state. Pass: Icon, Title, Message, optional Action (safe HTML). */}}
{{define "empty"}}
<div class="empty-state text-center py-5">
<i class="bi {{.Icon}} empty-icon"></i>
<h2 class="h5 mt-3 mb-2">{{.Title}}</h2>
<p class="text-body-secondary mx-auto" style="max-width: 34rem;">{{.Message}}</p>
{{if .Action}}<div class="mt-3">{{.Action}}</div>{{end}}
</div>
{{end}}
{{/* Pagination. Pass: P (pagination struct), Q (url.Values). */}}
{{define "pagination"}}
{{$p := .P}}{{$q := .Q}}
{{if gt $p.Pages 1}}
<nav class="d-flex flex-wrap justify-content-between align-items-center gap-2 mt-3" aria-label="Pagination">
<div class="text-body-secondary small">
Showing {{num $p.From}}{{num $p.To}} of {{num $p.Total}}
</div>
<ul class="pagination pagination-sm mb-0">
<li class="page-item {{if not $p.HasPrev}}disabled{{end}}">
<a class="page-link" href="{{withQuery $q "page" 1}}" aria-label="First page">&laquo;</a>
</li>
<li class="page-item {{if not $p.HasPrev}}disabled{{end}}">
<a class="page-link" href="{{withQuery $q "page" (sub $p.Page 1)}}" aria-label="Previous page">&lsaquo;</a>
</li>
{{range $n := pages $p.Page $p.Pages}}
<li class="page-item {{if eq $n $p.Page}}active{{end}}">
<a class="page-link" href="{{withQuery $q "page" $n}}">{{$n}}</a>
</li>
{{end}}
<li class="page-item {{if not $p.HasNext}}disabled{{end}}">
<a class="page-link" href="{{withQuery $q "page" (add $p.Page 1)}}" aria-label="Next page">&rsaquo;</a>
</li>
<li class="page-item {{if not $p.HasNext}}disabled{{end}}">
<a class="page-link" href="{{withQuery $q "page" $p.Pages}}" aria-label="Last page">&raquo;</a>
</li>
</ul>
</nav>
{{else if gt $p.Total 0}}
<div class="text-body-secondary small mt-3">{{num $p.Total}} result{{if ne $p.Total 1}}s{{end}}</div>
{{end}}
{{end}}
{{/* A destructive submit button that opens the shared confirmation modal.
Pass: Action, CSRF, Label, Message, optional Icon, Class, ReturnTo. */}}
{{define "confirmform"}}
<form method="post" action="{{.Action}}" class="d-inline js-confirm" data-confirm="{{.Message}}">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
{{if .ReturnTo}}<input type="hidden" name="return_to" value="{{.ReturnTo}}">{{end}}
{{range $k, $v := .Fields}}<input type="hidden" name="{{$k}}" value="{{$v}}">{{end}}
<button type="submit" class="{{if .Class}}{{.Class}}{{else}}btn btn-sm btn-outline-danger{{end}}"
{{if .Title}}title="{{.Title}}"{{end}}>
{{if .Icon}}<i class="bi {{.Icon}}"></i>{{end}}{{if .Label}} {{.Label}}{{end}}
</button>
</form>
{{end}}
{{/* A plain post button with no confirmation. Same fields as confirmform. */}}
{{define "postform"}}
<form method="post" action="{{.Action}}" class="d-inline">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
{{if .ReturnTo}}<input type="hidden" name="return_to" value="{{.ReturnTo}}">{{end}}
{{range $k, $v := .Fields}}<input type="hidden" name="{{$k}}" value="{{$v}}">{{end}}
<button type="submit" class="{{if .Class}}{{.Class}}{{else}}btn btn-sm btn-outline-secondary{{end}}"
{{if .Title}}title="{{.Title}}"{{end}}>
{{if .Icon}}<i class="bi {{.Icon}}"></i>{{end}}{{if .Label}} {{.Label}}{{end}}
</button>
</form>
{{end}}
{{/* Settings sub-navigation. Pass the active key as .Subnav. */}}
{{define "settingsnav"}}
<ul class="nav nav-pills settings-nav mb-4 flex-wrap gap-1">
<li class="nav-item"><a class="nav-link {{if eq .Subnav "dns"}}active{{end}}" href="/settings/dns">DNS</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "resolver"}}active{{end}}" href="/settings/resolver">Resolver</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "cache"}}active{{end}}" href="/settings/cache">Cache</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "http"}}active{{end}}" href="/settings/http">Web Server</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "blockpage"}}active{{end}}" href="/settings/blockpage">Block Page</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "logging"}}active{{end}}" href="/settings/logging">Logging</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "database"}}active{{end}}" href="/settings/database">Database</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "api"}}active{{end}}" href="/settings/api">API</a></li>
<li class="nav-item"><a class="nav-link {{if eq .Subnav "account"}}active{{end}}" href="/account">Authentication</a></li>
</ul>
{{end}}
{{/* Marks a field that needs a restart to take effect. */}}
{{define "restartbadge"}}
<span class="badge text-bg-warning-subtle border border-warning-subtle text-warning-emphasis ms-1"
title="Changing this requires a restart">restart required</span>
{{end}}