fix service record display

This commit is contained in:
Hannaeko 2025-04-30 10:38:03 +02:00
parent 95d38c5514
commit 06fddae344
10 changed files with 188 additions and 43 deletions

View file

@ -21,6 +21,7 @@ html, body {
main { main {
max-width: 55rem; max-width: 55rem;
margin: auto; margin: auto;
padding: .5rem
} }
h1 { h1 {
@ -177,3 +178,42 @@ a.button:focus-visible {
color: white; color: white;
background-color: #850085; background-color: #850085;
} }
form h3 {
margin-top: 0;
}
fieldset {
margin: 0;
padding: 0;
border: none;
}
legend {
padding: 0;
margin-bottom: .5rem;
font-size: 1.1rem;
}
input[type="text"] {
font-size: 1rem;
}
textarea {
resize: vertical;
}
.form-input {
display: flex;
flex-direction: column;
gap: .5rem;
flex-grow: 1;
margin-bottom: 1rem;
}
.form-row {
display: flex;
width: 100%;
gap: 1rem;
flex-wrap: wrap;
}

View file

@ -12,6 +12,8 @@ example.com. 600 IN AAAA 2001:db8:cafe:bc68::2
example.com. 3600 IN MX 1 srv1.example.com. example.com. 3600 IN MX 1 srv1.example.com.
example.com. 3600 IN MX 100 mail.example.net. example.com. 3600 IN MX 100 mail.example.net.
_imap._tcp.example.com. 3600 IN SRV 1 1 143 mail.example.net.
example.com. 84600 IN NS ns.example.com. example.com. 84600 IN NS ns.example.com.
ns.example.com. 84600 IN A 198.51.100.3 ns.example.com. 84600 IN A 198.51.100.3

View file

@ -1,3 +1,5 @@
## Zone content
zone-header = Zone { $zone_name } zone-header = Zone { $zone_name }
zone-content-title = Zone content zone-content-title = Zone content
@ -18,4 +20,12 @@ zone-content-record-type-mailserver =
zone-content-record-type-nameserver = zone-content-record-type-nameserver =
.type-name = Name servers .type-name = Name servers
zone-content-record-type-service =
.type-name = Services
.data-address = Address: { $address }
.data-priority = Priority: { $priority }
.data-weight = Weight: { $weight }
zone-content-new-record-button = New record zone-content-new-record-button = New record
## Create record

View file

@ -1,3 +1,4 @@
## Zone content
zone-header = Zone { $zone_name } zone-header = Zone { $zone_name }
@ -19,4 +20,12 @@ zone-content-record-type-mailserver =
zone-content-record-type-nameserver = zone-content-record-type-nameserver =
.type-name = Serveurs de noms .type-name = Serveurs de noms
zone-content-record-type-service =
.type-name = Services
.data-address = Adresse : { $address }
.data-priority = Priorité : { $priority }
.data-weight = Poids : { $weight }
zone-content-new-record-button = Nouvel enregistrement zone-content-new-record-button = Nouvel enregistrement
## Create record

View file

@ -90,6 +90,7 @@ impl A {
pub fn validate(self) -> Result<internal::A, Vec<Error>> { pub fn validate(self) -> Result<internal::A, Vec<Error>> {
let mut errors = Vec::new(); let mut errors = Vec::new();
// TODO: replace with custom validation
let address = push_error!(self.address.parse::<Ipv4Addr>().map_err(|e| { let address = push_error!(self.address.parse::<Ipv4Addr>().map_err(|e| {
Error::from(RDataValidationError::Ip4Address { input: self.address }) Error::from(RDataValidationError::Ip4Address { input: self.address })
.with_cause(&e.to_string()) .with_cause(&e.to_string())

View file

@ -201,7 +201,7 @@ impl From<internal::RecordList> for FriendlyRecords {
continue; continue;
} }
let rdata = rdata.unwrap(); let (name, rdata) = rdata.unwrap();
if let FriendlyRData::Alias(alias) = rdata { if let FriendlyRData::Alias(alias) = rdata {
records.aliases.push(alias) records.aliases.push(alias)

View file

@ -32,13 +32,13 @@ impl RData {
} }
} }
pub fn friendly(self, owner: &str, ttl: u32) -> Option<friendly::FriendlyRData> { pub fn friendly(self, owner: &str, ttl: u32) -> Option<(String, friendly::FriendlyRData)> {
match self { match self {
RData::A(data) => data.friendly(), RData::A(data) => data.friendly(owner),
RData::Aaaa(data) => data.friendly(), RData::Aaaa(data) => data.friendly(owner),
RData::Cname(data) => data.friendly(owner, ttl), RData::Cname(data) => data.friendly(owner, ttl),
RData::Mx(data) => data.friendly(), RData::Mx(data) => data.friendly(owner),
RData::Ns(data) => data.friendly(), RData::Ns(data) => data.friendly(owner),
RData::Srv(data) => data.friendly(owner), RData::Srv(data) => data.friendly(owner),
_ => None, _ => None,
} }
@ -51,10 +51,10 @@ pub struct A {
} }
impl A { impl A {
pub fn friendly(self) -> Option<friendly::FriendlyRData> { pub fn friendly(self, owner: &str) -> Option<(String, friendly::FriendlyRData)> {
Some(friendly::FriendlyRData::Address(friendly::Address { Some((owner.to_string(), friendly::FriendlyRData::Address(friendly::Address {
address: self.address.to_string() address: self.address.to_string()
})) })))
} }
} }
@ -64,10 +64,10 @@ pub struct Aaaa {
} }
impl Aaaa { impl Aaaa {
pub fn friendly(self) -> Option<friendly::FriendlyRData> { pub fn friendly(self, owner: &str) -> Option<(String, friendly::FriendlyRData)> {
Some(friendly::FriendlyRData::Address(friendly::Address { Some((owner.to_string(), friendly::FriendlyRData::Address(friendly::Address {
address: self.address.to_string() address: self.address.to_string()
})) })))
} }
} }
@ -77,12 +77,12 @@ pub struct Cname {
} }
impl Cname { impl Cname {
pub fn friendly(self, owner: &str, ttl: u32) -> Option<friendly::FriendlyRData> { pub fn friendly(self, owner: &str, ttl: u32) -> Option<(String, friendly::FriendlyRData)> {
Some(friendly::FriendlyRData::Alias(friendly::Alias { Some((owner.to_string(), friendly::FriendlyRData::Alias(friendly::Alias {
from: owner.into(), from: owner.into(),
target: self.target.to_string(), target: self.target.to_string(),
ttl: ttl.into(), ttl: ttl.into(),
})) })))
} }
} }
@ -93,11 +93,11 @@ pub struct Mx {
} }
impl Mx { impl Mx {
pub fn friendly(self) -> Option<friendly::FriendlyRData> { pub fn friendly(self, owner: &str) -> Option<(String, friendly::FriendlyRData)> {
Some(friendly::FriendlyRData::MailServer(friendly::MailServer { Some((owner.to_string(), friendly::FriendlyRData::MailServer(friendly::MailServer {
preference: self.preference.into(), preference: self.preference.into(),
mail_exchanger: self.mail_exchanger.to_string(), mail_exchanger: self.mail_exchanger.to_string(),
})) })))
} }
} }
@ -107,10 +107,10 @@ pub struct Ns {
} }
impl Ns { impl Ns {
pub fn friendly(self) -> Option<friendly::FriendlyRData> { pub fn friendly(self, owner: &str) -> Option<(String, friendly::FriendlyRData)> {
Some(friendly::FriendlyRData::NameServer(friendly::NameServer { Some((owner.to_string(), friendly::FriendlyRData::NameServer(friendly::NameServer {
target: self.target.to_string(), target: self.target.to_string(),
})) })))
} }
} }
@ -139,7 +139,7 @@ pub struct Srv {
} }
impl Srv { impl Srv {
pub fn friendly(self, owner: &str) -> Option<friendly::FriendlyRData> { pub fn friendly(self, owner: &str) -> Option<(String, friendly::FriendlyRData)> {
let labels: Vec<_> = owner.splitn(3, '.').collect(); let labels: Vec<_> = owner.splitn(3, '.').collect();
if labels.len() != 3 { if labels.len() != 3 {
None None
@ -147,7 +147,7 @@ impl Srv {
let service_name = labels[0]. strip_prefix('_'); let service_name = labels[0]. strip_prefix('_');
let protocol = labels[1]. strip_prefix('_'); let protocol = labels[1]. strip_prefix('_');
if let (Some(service_name), Some(protocol)) = (service_name, protocol) { if let (Some(service_name), Some(protocol)) = (service_name, protocol) {
Some(friendly::FriendlyRData::Service(friendly::Service { Some((labels[2].to_string(), friendly::FriendlyRData::Service(friendly::Service {
service_type: friendly::ServiceType::Other, service_type: friendly::ServiceType::Other,
service_name: Some(service_name.into()), service_name: Some(service_name.into()),
service_protocol: Some(protocol.into()), service_protocol: Some(protocol.into()),
@ -155,7 +155,7 @@ impl Srv {
weight: self.weight.into(), weight: self.weight.into(),
priority: self.priority.into(), priority: self.priority.into(),
server: self.server.to_string(), server: self.server.to_string(),
})) })))
} else { } else {
None None
} }

View file

@ -3,12 +3,6 @@
<div class="rtype"> <div class="rtype">
{{ tr(msg="zone-content-record-type-" ~ rtype, attr="type-name", lang=lang) }} {{ tr(msg="zone-content-record-type-" ~ rtype, attr="type-name", lang=lang) }}
<div class="action"> <div class="action">
<button class="icon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z"/>
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z"/>
</svg>
</button>
<a class="button icon" href="#"> <a class="button icon" href="#">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325"/> <path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325"/>
@ -35,7 +29,11 @@
</div> </div>
<div class="rdata-complementary"> <div class="rdata-complementary">
<span class="pill"> <span class="pill">
{{ tr(msg="zone-content-record-type-mailserver", attr="data-preference", preference=data.preference, lang=lang) }} {{ tr(
msg="zone-content-record-type-mailserver",
attr="data-preference",
preference=data.preference,
lang=lang) }}
</span> </span>
</div> </div>
{% elif rtype == "nameserver" %} {% elif rtype == "nameserver" %}
@ -54,15 +52,27 @@
{% endif %} {% endif %}
</span> </span>
<span class="pill"> <span class="pill">
Address: {{ data.server }}:{{ data.port }} {{ tr(
msg="zone-content-record-type-service",
attr="data-address",
address=data.server ~ ":" ~ data.port,
lang=lang) }}
</span> </span>
</div> </div>
<div class="rdata-complementary"> <div class="rdata-complementary">
<span class="pill"> <span class="pill">
Priority: {{ data.priority }} {{ tr(
msg="zone-content-record-type-service",
attr="data-priority",
priority=data.priority,
lang=lang) }}
</span> </span>
<span class="pill"> <span class="pill">
Weight: {{ data.weight }} {{ tr(
msg="zone-content-record-type-service",
attr="data-weight",
weight=data.weight,
lang=lang) }}
</span> </span>
</div> </div>
{% endif %} {% endif %}

View file

@ -1,4 +1,5 @@
{% extends "bases/app.html" %} {% extends "bases/app.html" %}
{% import "macros/new_rrset.html" as new_rrset %}
{% block title %}New record - {{ current_zone }} - {% endblock title %} {% block title %}New record - {{ current_zone }} - {% endblock title %}
{% block main %} {% block main %}
@ -20,14 +21,14 @@
{% if domain_error %} {% if domain_error %}
<p class="error" id="subdomain-error">{{ domain_error.description }}</p> <p class="error" id="subdomain-error">{{ domain_error.description }}</p>
{% endif %} {% endif %}
<p id="subdomain-help">Only the subdomain, without the main domain. For instance, "www" to create the subdomain "www.{{ current_zone }}".</p> <p id="subdomain-help">Only the subdomain, without the parent domain. For instance, "www" to create the subdomain "www.{{ current_zone }}".</p>
<button type="submit">Next step</button> <button type="submit">Next step</button>
</form> </form>
{% elif not config and not rtype %} {% elif not config and not rtype %}
<h2>Configure the domain {{ new_record_name }}...</h2> <h2>Configure the domain {{ new_record_name }}...</h2>
<ul> <ul>
<li><a href="{{ url }}&config=web">Web</a></li> <li><a href="{{ url }}&config=web">Web site</a></li>
<li><a href="{{ url }}&config=mail">E-mails</a></li> <li><a href="{{ url }}&config=mail">E-mails</a></li>
</ul> </ul>
<h2>...or create a new record for the domain {{ new_record_name }}</h2> <h2>...or create a new record for the domain {{ new_record_name }}</h2>
@ -39,10 +40,10 @@
<li><a href="{{ url }}&rtype=service">Service (SRV)</a></li> <li><a href="{{ url }}&rtype=service">Service (SRV)</a></li>
</ul> </ul>
<h3>E-mails</h3> <h3>E-mails</h3>
<ul> <ul>sdv
<li><a href="{{ url }}&rtype=service">Mail servers (MX)</a></li> <li><a href="{{ url }}&rtype=service">Mail servers (MX)</a></li>
<li><a href="{{ url }}&rtype=spf">Sender policy (SPF)</a></li> <li><a href="{{ url }}&rtype=spf">Sender policy (SPF)</a></li>
<li><a href="{{ url }}&rtype=dkim">Cryptographic signatures (DKIM)</a></li> <li><a href="{{ url }}&rtype=dkim">Cryptographic signature (DKIM)</a></li>
<li><a href="{{ url }}&rtype=dmarc">Error reporting (DMARC)</a></li> <li><a href="{{ url }}&rtype=dmarc">Error reporting (DMARC)</a></li>
</ul> </ul>
<h3>Security</h3> <h3>Security</h3>
@ -56,6 +57,78 @@
<li><a href="{{ url }}&rtype=ds">Delegation signer (DS)</a></li> <li><a href="{{ url }}&rtype=ds">Delegation signer (DS)</a></li>
</ul> </ul>
{% else %} {% else %}
{% if config == "web" %}
<h2>Configure a web site for the domain <strong>{{ new_record_name }}</strong></h2>
<form>
<h3>Web servers</h3>
<div class="form-input">
<label for="address">IP Address #1</label>
<input name="address" id="address" type="text">
</div>
<button type="submit">Save configuration</button>
</form>
{% elif config == "mail" %}
<h2>Configure e-mails for the domain <strong>{{ new_record_name }}</strong></h2>
<form>
<h3>Mail servers</h3>
<fieldset>
<legend>Mail server #1</legend>
<div class="form-row">
<div class="form-input">
<label for="server">Server name</label>
<input name="server" id="server" type="text">
</div>
<div class="form-input">
<label for="preference">Preference</label>
<input name="preference" id="preference" type="text">
</div>
</div>
</fieldset>
<h3>Security</h3>
<div class="form-input">
<label for="spf">Sender policy (SPF)</label>
<input name="spf" id="spf" type="text">
</div>
<div class="form-input">
<label for="dmarc">Error reporting policy (DMARC)</label>
<input name="dmarc" id="dmarc" type="text">
</div>
<fieldset>
<legend>Cryptographic signature (DKIM) #1</legend>
<div class="form-row">
<div class="form-input">
<label for="dkim-selector">Selector</label>
<input name="dkim-selector" id="dkim-selector" type="text">
</div>
<div class="form-input">
<label for="dkim-key">Signing key</label>
<textarea name="dkim-key" id="dkim-key"></textarea>
</div>
</div>
</fieldset>
<button type="submit">Save configuration</button>
</form>
{% endif %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -1,4 +1,4 @@
{% import "macros.html" as macros %} {% import "macros/display_rrset.html" as rrset %}
{% extends "bases/app.html" %} {% extends "bases/app.html" %}
{% block title %}{{ tr(msg="zone-content-title", lang=lang) }} - {{ current_zone }} - {% endblock title %} {% block title %}{{ tr(msg="zone-content-title", lang=lang) }} - {{ current_zone }} - {% endblock title %}
@ -32,7 +32,7 @@
<h4>{{ tr(msg="zone-content-section-web-header", lang=lang) }}</h4> <h4>{{ tr(msg="zone-content-section-web-header", lang=lang) }}</h4>
<ul> <ul>
{% if group.web.addresses %} {% if group.web.addresses %}
{{ macros::rrset( {{ rrset::rrset(
rtype=group.web.addresses.rtype, rtype=group.web.addresses.rtype,
ttl=group.web.addresses.ttl, ttl=group.web.addresses.ttl,
data=group.web.addresses.data, data=group.web.addresses.data,
@ -46,7 +46,7 @@
<h4>{{ tr(msg="zone-content-section-mail-header", lang=lang) }}</h4> <h4>{{ tr(msg="zone-content-section-mail-header", lang=lang) }}</h4>
<ul> <ul>
{% if group.mail.servers %} {% if group.mail.servers %}
{{ macros::rrset( {{ rrset::rrset(
rtype=group.mail.servers.rtype, rtype=group.mail.servers.rtype,
ttl=group.mail.servers.ttl, ttl=group.mail.servers.ttl,
data=group.mail.servers.data, data=group.mail.servers.data,
@ -54,7 +54,7 @@
lang=lang) }} lang=lang) }}
{% endif %} {% endif %}
{% if group.mail.spf %} {% if group.mail.spf %}
{{ macros::rrset( {{ rrset::rrset(
rtype=group.mail.spf.rtype, rtype=group.mail.spf.rtype,
ttl=group.mail.spf.ttl, ttl=group.mail.spf.ttl,
data=group.mail.spf.data, data=group.mail.spf.data,
@ -68,7 +68,7 @@
<h4>{{ tr(msg="zone-content-section-general-header", lang=lang) }}</h4> <h4>{{ tr(msg="zone-content-section-general-header", lang=lang) }}</h4>
<ul> <ul>
{% for rrset in group.general_records %} {% for rrset in group.general_records %}
{{ macros::rrset( {{ rrset::rrset(
rtype=rrset.rtype, rtype=rrset.rtype,
ttl=rrset.ttl, ttl=rrset.ttl,
data=rrset.data, data=rrset.data,