Files
hetzner-api-dyndns-records-…/internal/web/templates/domain_detail.html
T
2026-08-29 18:38:38 +02:00

125 lines
15 KiB
HTML

{{define "domain_detail"}}{{template "layout" .}}{{end}}
{{define "content"}}
<header class="page-heading">
<div><a class="back-link" href="/domains">← Domains</a><span class="page-kicker">DNS-Zone</span><h1>{{.Zone.Name}}</h1><p>{{if .IsAdmin}}DynDNS-Ziele und statische CNAME-Aliase dieser Zone.{{else}}Dir zugewiesene DynDNS-Ziele dieser Zone.{{end}}</p></div>
{{if .IsAdmin}}<form action="/domains/{{.Zone.ID}}/import" method="post"><input type="hidden" name="csrf" value="{{.CSRF}}"><button class="button button-quiet" type="submit">Records neu einlesen</button></form>{{end}}
</header>
<section class="zone-summary" aria-label="Zonenstatus">
<div><span>Provider</span><strong>Hetzner Cloud DNS</strong></div>
<div><span>Letzter Import</span><strong><time data-local-time="{{unixSeconds .Zone.LastSyncAt}}">{{formatTime .Zone.LastSyncAt}}</time></strong></div>
<div><span>Verknüpfte Ziele</span><strong>{{len .Targets}}</strong></div>
{{if .IsAdmin}}<div><span>Getrennte Ziele</span><strong>{{len .ArchivedTargets}}</strong></div>{{end}}
</section>
{{if .IsAdmin}}
<section class="bulk-ttl-panel" aria-label="TTL für verwaltete Records setzen">
<form action="/domains/{{.Zone.ID}}/ttl" method="post" class="bulk-ttl-form"><input type="hidden" name="csrf" value="{{.CSRF}}"><div><span class="section-label">Sammelaktion</span><strong>TTL für alle verwalteten Records</strong><small>A-/AAAA-Ziele und CNAMEs dieser Oberfläche; andere Hetzner-Records bleiben unverändert.</small></div><label>TTL <select name="ttl" required>{{range ttlOptions}}<option value="{{.}}" {{if eq . 60}}selected{{end}}>{{.}} Sekunden</option>{{end}}</select></label><button class="button button-quiet" type="submit">TTL für alle setzen</button></form>
</section>
{{end}}
<section class="targets-section">
<div class="section-heading"><div><span class="section-label">Verknüpfte Records</span><h2>Subdomains und Root-Record</h2></div><span class="record-count">{{len .Targets}} Einträge</span></div>
{{if .Targets}}
<div class="target-list">
{{range .Targets}}
{{$target := .}}
{{$canManage := or $.IsAdmin (index $.ManageTargetIDs .ID)}}
<article class="target-row {{if .Active}}is-active{{end}}">
<div class="target-main"><span class="target-status {{if .Active}}on{{end}}" aria-label="{{if .Active}}aktiv{{else}}inaktiv{{end}}"></span><div><h3>{{.FQDN}}</h3><p><span class="family-chip">{{families .}}</span><span>{{if .Description}}{{.Description}}{{else}}Keine Beschreibung{{end}}</span></p></div></div>
<div class="target-meta"><time data-local-time="{{unixSeconds .LastUpdatedAt}}">{{formatTime .LastUpdatedAt}}</time><span class="status-pill {{statusClass .LastStatus}}">{{if .LastStatus}}{{.LastStatus}}{{else}}Nicht ausgeführt{{end}}</span></div>
{{if $canManage}}<details class="target-edit"><summary>Einstellungen <span>⌄</span></summary><div class="edit-inner">
<form action="/targets/{{.ID}}/save" method="post" class="target-form" data-target-form data-ipv6-enabled="{{$.Settings.IPv6Enabled}}" data-update-ipv4="{{.UpdateIPv4}}" data-update-ipv6="{{.UpdateIPv6}}">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<div class="toggle-row"><label class="switch"><input type="checkbox" name="active" data-target-active {{if .Active}}checked{{end}}><span></span> DynDNS aktiv</label>{{if $.IsAdmin}}<label class="switch"><input type="checkbox" name="update_ipv4" {{if .UpdateIPv4}}checked{{end}}><span></span> IPv4 / A</label><label class="switch {{if not $.Settings.IPv6Enabled}}switch-disabled{{end}}"><input type="checkbox" name="update_ipv6" {{if and .UpdateIPv6 $.Settings.IPv6Enabled}}checked{{end}} {{if not $.Settings.IPv6Enabled}}disabled{{end}}><span></span> IPv6 / AAAA{{if not $.Settings.IPv6Enabled}} pausiert{{end}}</label>{{end}}</div>
<div class="form-grid" data-static-ip-fields {{if .Active}}hidden{{end}} aria-hidden="{{if .Active}}true{{else}}false{{end}}"><label>Feste IPv4 <input name="static_ipv4" data-static-ipv4 data-current-ipv4-placeholder value="{{.StaticIPv4}}" inputmode="decimal" placeholder="{{if $.Settings.LastIPv4}}{{$.Settings.LastIPv4}}{{else}}203.0.113.10{{end}}" {{if .Active}}disabled{{else if .UpdateIPv4}}required{{else}}disabled{{end}}><small>Pflichtfeld, wenn IPv4 aktiv und DynDNS aus ist; grau steht die zuletzt ermittelte aktuelle IPv4.</small></label><label class="{{if not $.Settings.IPv6Enabled}}switch-disabled{{end}}">Feste IPv6 <input name="static_ipv6" data-static-ipv6 value="{{.StaticIPv6}}" inputmode="text" placeholder="2001:db8::10" {{if or .Active (not $.Settings.IPv6Enabled)}}disabled{{else if .UpdateIPv6}}required{{else}}disabled{{end}}><small>Pflichtfeld, wenn IPv6 aktiv und DynDNS aus ist.</small></label></div>
<p class="target-requirement" data-target-requirement hidden>Trage für jede aktive IP-Familie eine feste Adresse ein oder aktiviere DynDNS.</p>
<div class="form-grid"><label>Beschreibung <input name="description" value="{{.Description}}" maxlength="512" placeholder="Interne Beschreibung"></label><label>TTL <select name="ttl" required>{{if not (supportedTTL $target.TTL)}}<option value="{{$target.TTL}}" selected>{{$target.TTL}} Sekunden (bestehend)</option>{{end}}{{range ttlOptions}}<option value="{{.}}" {{if eq . $target.TTL}}selected{{end}}>{{.}} Sekunden</option>{{end}}</select></label></div>
<div class="form-actions"><button class="button button-primary" type="submit">Speichern</button></div>
</form>
{{if $.IsAdmin}}
<div class="danger-actions"><form action="/targets/{{.ID}}/archive" method="post" class="unlink-form">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<strong>Verknüpfung trennen</strong>
<button class="button button-danger-soft" type="submit">Verknüpfung trennen</button>
</form><form action="/targets/{{.ID}}/delete" method="post" class="unlink-form">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<strong>Ziel vollständig löschen</strong>
<button class="button button-danger-soft" type="submit">Ziel löschen</button>
</form></div>
{{end}}
</div></details>{{end}}
{{if .LastError}}<p class="row-error">{{.LastError}}</p>{{end}}
</article>
{{end}}
</div>
{{else}}<div class="empty-state">Keine verknüpften A-/AAAA-Records vorhanden. {{if or .IsAdmin .CanCreateTargets}}Lege unten eine neue Subdomain an.{{end}}</div>{{end}}
</section>
{{if .IsAdmin}}
<section class="targets-section">
<div class="section-heading"><div><span class="section-label">Statische Records</span><h2>CNAME-Aliase</h2></div><span class="record-count">{{len .CNAMERecords}} Einträge</span></div>
{{if .CNAMERecords}}
<div class="target-list">
{{range .CNAMERecords}}
{{$cname := .}}
<article class="target-row {{if .Active}}is-active{{end}}">
<div class="target-main"><span class="target-status {{if .Active}}on{{end}}" aria-label="{{if .Active}}aktiv{{else}}inaktiv{{end}}"></span><div><h3>{{.FQDN}}</h3><p><span class="family-chip">CNAME</span><span>→ {{.Destination}}</span>{{if .Description}}<span>· {{.Description}}</span>{{end}}</p></div></div>
<div class="target-meta"><span class="status-pill {{if .Active}}status-good{{end}}">{{if .Active}}Aktiv{{else}}Inaktiv{{end}}</span></div>
<details class="target-edit"><summary>Bearbeiten <span>⌄</span></summary><div class="edit-inner">
<form action="/cnames/{{.ID}}/save" method="post" class="target-form">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<div class="toggle-row"><label class="switch"><input type="checkbox" name="active" {{if .Active}}checked{{end}}><span></span> CNAME aktiv</label></div>
<div class="form-grid cname-edit-grid"><label>CNAME-Ziel <input name="destination" value="{{.Destination}}" maxlength="253" required></label><label>TTL <select name="ttl" required>{{if not (supportedTTL $cname.TTL)}}<option value="{{$cname.TTL}}" selected>{{$cname.TTL}} Sekunden (bestehend)</option>{{end}}{{range ttlOptions}}<option value="{{.}}" {{if eq . $cname.TTL}}selected{{end}}>{{.}} Sekunden</option>{{end}}</select></label></div><small class="field-note">Vollständiger Domainname; der abschließende Punkt wird ergänzt.</small>
<label>Beschreibung <input name="description" value="{{.Description}}" maxlength="512" placeholder="Interne Beschreibung"></label>
<div class="form-actions"><button class="button button-primary" type="submit">Speichern</button></div>
</form>
<div class="danger-actions"><form action="/cnames/{{.ID}}/remove" method="post" class="unlink-form">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<strong>Aus WebUI entfernen</strong>
<button class="button button-danger-soft" type="submit">Aus WebUI entfernen</button>
</form><form action="/cnames/{{.ID}}/delete" method="post" class="unlink-form">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<strong>Ziel vollständig löschen</strong>
<button class="button button-danger-soft" type="submit">Ziel löschen</button>
</form></div>
</div></details>
</article>
{{end}}
</div>
{{else}}<div class="empty-state">Keine CNAME-Aliase vorhanden. Lege unten einen neuen Alias an.</div>{{end}}
</section>
{{end}}
{{if and .IsAdmin .ArchivedTargets}}
<section class="panel archive-panel">
<div class="panel-heading"><div><span class="section-label">Archiv</span><h2>Getrennte Ziele</h2></div><span class="record-count">{{len .ArchivedTargets}} Einträge</span></div>
<p class="panel-description">Diese Einträge sind nicht mehr mit dem Updater verknüpft. Die zugehörigen Hetzner-DNS-Records wurden beim Trennen nicht gelöscht.</p>
<div class="archive-list">
{{range .ArchivedTargets}}
<div class="archive-row"><span><strong>{{.FQDN}}</strong><small>{{families .}} · getrennt <time data-local-time="{{unixSeconds .ArchivedAt}}">{{formatTime .ArchivedAt}}</time></small></span><form action="/targets/{{.ID}}/restore" method="post"><input type="hidden" name="csrf" value="{{$.CSRF}}"><button class="button button-quiet" type="submit">Wieder verknüpfen</button></form></div>
{{end}}
</div>
</section>
{{end}}
{{if or .IsAdmin .CanCreateTargets}}
<section class="action-grid admin-actions-grid">
<article class="panel"><div class="panel-heading"><div><span class="section-label">Record anlegen</span><h2>Neue Subdomain</h2></div></div>
<form action="/targets" method="post" class="stacked-form" data-target-form data-ipv6-enabled="{{.Settings.IPv6Enabled}}" data-update-ipv4="true" data-update-ipv6="false"><input type="hidden" name="csrf" value="{{.CSRF}}"><input type="hidden" name="zone_id" value="{{.Zone.ID}}"><label>{{if .IsAdmin}}Name oder Root-Record{{else}}Subdomain{{end}} <input name="record_name" required placeholder="{{if .IsAdmin}}home oder @{{else}}home{{end}}"><small>Die Zone {{.Zone.Name}} wird automatisch ergänzt.</small></label><div class="toggle-row"><label class="switch"><input type="checkbox" name="active" data-target-active checked><span></span> DynDNS aktivieren</label><label class="switch"><input type="checkbox" name="update_ipv4" checked><span></span> IPv4</label><label class="switch {{if not .Settings.IPv6Enabled}}switch-disabled{{end}}"><input type="checkbox" name="update_ipv6" {{if not .Settings.IPv6Enabled}}disabled{{end}}><span></span> IPv6{{if not .Settings.IPv6Enabled}} pausiert{{end}}</label></div><div class="form-grid" data-static-ip-fields hidden aria-hidden="true"><label>Feste IPv4 <input name="static_ipv4" data-static-ipv4 data-current-ipv4-placeholder inputmode="decimal" placeholder="{{if .Settings.LastIPv4}}{{.Settings.LastIPv4}}{{else}}203.0.113.10{{end}}" disabled><small>Pflichtfeld, wenn IPv4 aktiv und DynDNS aus ist; grau steht die zuletzt ermittelte aktuelle IPv4.</small></label><label class="{{if not .Settings.IPv6Enabled}}switch-disabled{{end}}">Feste IPv6 <input name="static_ipv6" data-static-ipv6 inputmode="text" placeholder="2001:db8::10" disabled><small>Pflichtfeld, wenn IPv6 aktiv und DynDNS aus ist.</small></label></div><p class="target-requirement" data-target-requirement hidden>Trage für jede aktive IP-Familie eine feste Adresse ein oder aktiviere DynDNS.</p><div class="form-grid"><label>Beschreibung <input name="description" maxlength="512" placeholder="Interne Beschreibung"></label><label>TTL <select name="ttl" required>{{range ttlOptions}}<option value="{{.}}" {{if eq . 60}}selected{{end}}>{{.}} Sekunden</option>{{end}}</select></label></div><button class="button button-primary" type="submit">Record anlegen</button></form>
</article>
{{if .IsAdmin}}
<article class="panel"><div class="panel-heading"><div><span class="section-label">Statischer Record</span><h2>Neuen CNAME anlegen</h2></div></div>
<form action="/cnames" method="post" class="stacked-form"><input type="hidden" name="csrf" value="{{.CSRF}}"><input type="hidden" name="zone_id" value="{{.Zone.ID}}"><div class="form-grid"><label>Alias <input name="record_name" required placeholder="www"><small>Die Zone {{.Zone.Name}} wird automatisch ergänzt.</small></label><label>CNAME-Ziel <input name="destination" required placeholder="app.example.net."><small>Vollständiger Domainname; ein abschließender Punkt ist optional.</small></label></div><div class="form-grid"><label>Beschreibung <input name="description" maxlength="512" placeholder="Interne Beschreibung"></label><label>TTL <select name="ttl" required>{{range ttlOptions}}<option value="{{.}}" {{if eq . 60}}selected{{end}}>{{.}} Sekunden</option>{{end}}</select></label></div><div class="toggle-row"><label class="switch"><input type="checkbox" name="active" checked><span></span> CNAME aktivieren</label></div><button class="button button-primary" type="submit">CNAME anlegen</button></form>
</article>
<article class="panel"><div class="panel-heading"><div><span class="section-label">Zonenzugang</span><h2>Verbindung verwalten</h2></div></div>
<p class="panel-description">Der API-Token bleibt verschlüsselt. Eine Anzeige erfordert erneut das Admin-Passwort.</p>
<details><summary class="button button-quiet">API-Token anzeigen</summary><form action="/domains/{{.Zone.ID}}/token" method="post" class="stacked-form inset-form"><input type="hidden" name="csrf" value="{{.CSRF}}"><label>Admin-Passwort <input type="password" name="password" autocomplete="current-password" required></label><button class="button button-danger-soft" type="submit">Token entschlüsseln</button></form></details>
<div class="panel-divider"></div>
<details data-scroll-page-end><summary class="text-danger">Domain-Verbindung entfernen</summary><div class="danger-actions inset-form"><form action="/domains/{{.Zone.ID}}/remove" method="post" class="unlink-form"><input type="hidden" name="csrf" value="{{.CSRF}}"><strong>Nur aus der WebUI entfernen</strong><p>Löscht ausschließlich die lokale Konfiguration. Alle DNS-Records bei Hetzner bleiben bestehen.</p><button class="button button-danger-soft" type="submit">Lokale Verbindung entfernen</button></form><form action="/domains/{{.Zone.ID}}/delete" method="post" class="unlink-form"><input type="hidden" name="csrf" value="{{.CSRF}}"><strong>Domain vollständig löschen</strong><p>Löscht alle hier verwalteten A-, AAAA- und CNAME-Einträge aus der WebUI sowie die zugehörigen Records bei Hetzner. Andere Record-Typen bleiben bestehen.</p><button class="button button-danger-soft" type="submit">Domain und Records löschen</button></form></div></details>
</article>
{{end}}
</section>
{{end}}
{{end}}