change record data structure
This commit is contained in:
parent
91bffe153a
commit
cfdd9afc0e
10 changed files with 440 additions and 422 deletions
|
@ -11,18 +11,18 @@ zone-content-section-mail-header = E-mail
|
||||||
zone-content-section-services-header = Services
|
zone-content-section-services-header = Services
|
||||||
zone-content-section-general-header = General
|
zone-content-section-general-header = General
|
||||||
|
|
||||||
zone-content-record-type-address =
|
zone-content-record-type-addresses =
|
||||||
.type-name = IP addresses
|
.type-name = IP addresses
|
||||||
|
|
||||||
zone-content-record-type-mailserver =
|
zone-content-record-type-mailservers =
|
||||||
.type-name = E-mail servers
|
.type-name = E-mail servers
|
||||||
.data-preference = Preference: { $preference }
|
.data-preference = Preference: { $preference }
|
||||||
|
|
||||||
zone-content-record-type-nameserver =
|
zone-content-record-type-nameservers =
|
||||||
.type-name = Name servers
|
.type-name = Name servers
|
||||||
|
|
||||||
zone-content-record-type-service =
|
zone-content-record-type-service =
|
||||||
.type-name = Services
|
.type-name = Service
|
||||||
.data-priority = Priority: { $priority }
|
.data-priority = Priority: { $priority }
|
||||||
.data-weight = Weight: { $weight }
|
.data-weight = Weight: { $weight }
|
||||||
|
|
||||||
|
|
|
@ -1,190 +1,134 @@
|
||||||
use std::{collections::HashMap, fmt};
|
use std::{collections::HashMap, hash::Hash};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize, Serializer, ser::SerializeStruct};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::resources::dns::internal;
|
use crate::resources::dns::internal;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Deserialize, Serialize, Hash, Eq, PartialEq)]
|
||||||
#[serde(rename_all="lowercase")]
|
#[serde(rename_all="lowercase")]
|
||||||
pub enum FriendlyRType {
|
pub enum FriendlyRType {
|
||||||
Address,
|
Addresses,
|
||||||
Alias,
|
Alias,
|
||||||
MailServer,
|
MailServers,
|
||||||
NameServer,
|
NameServers,
|
||||||
Service,
|
Service,
|
||||||
Spf,
|
Spf,
|
||||||
TextData,
|
Texts,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FriendlyRType {
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
#[serde(rename_all="lowercase")]
|
||||||
match self {
|
pub enum RecordSection {
|
||||||
FriendlyRType::Address => write!(f, "address"),
|
Mail,
|
||||||
FriendlyRType::Alias => write!(f, "alias"),
|
Web,
|
||||||
FriendlyRType::MailServer => write!(f, "mailserver"),
|
Services,
|
||||||
FriendlyRType::NameServer => write!(f, "nameserver"),
|
Miscellaneous,
|
||||||
FriendlyRType::Service => write!(f, "service"),
|
|
||||||
FriendlyRType::Spf => write!(f, "spf"),
|
|
||||||
FriendlyRType::TextData => write!(f, "textdata"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait HasRType {
|
|
||||||
fn rtype(&self) -> FriendlyRType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(tag = "_type")]
|
|
||||||
pub enum FriendlyRData {
|
pub enum FriendlyRData {
|
||||||
Address(Address),
|
Address(Address),
|
||||||
Alias(Alias),
|
Alias(Alias),
|
||||||
MailServer(MailServer),
|
MailServer(MailServer),
|
||||||
NameServer(NameServer),
|
NameServer(NameServer),
|
||||||
Service(Service),
|
Service(ServiceSingleTarget),
|
||||||
Spf(Spf),
|
Spf(Spf),
|
||||||
TextData(TextData),
|
TextData(TextData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasRType for FriendlyRData {
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
fn rtype(&self) -> FriendlyRType {
|
#[serde(rename_all = "lowercase")]
|
||||||
match self {
|
pub enum FriendlyRDataAggregated {
|
||||||
FriendlyRData::Address(_) => FriendlyRType::Address,
|
Addresses(Addresses),
|
||||||
FriendlyRData::Alias(_) => FriendlyRType::Alias,
|
MailServers(MailServers),
|
||||||
FriendlyRData::MailServer(_) => FriendlyRType::MailServer,
|
NameServers(NameServers),
|
||||||
FriendlyRData::NameServer(_) => FriendlyRType::NameServer,
|
Service(Service),
|
||||||
FriendlyRData::Service(_) => FriendlyRType::Service,
|
Spf(Spf),
|
||||||
FriendlyRData::Spf(_) => FriendlyRType::Spf,
|
Texts(Texts),
|
||||||
FriendlyRData::TextData(_) => FriendlyRType::TextData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FriendlyRecord<T> {
|
pub struct FriendlyRecord {
|
||||||
ttl: i64,
|
ttl: i64,
|
||||||
data: T,
|
data: FriendlyRDataAggregated,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Serialize + HasRType> Serialize for FriendlyRecord<T> {
|
impl Serialize for FriendlyRecord {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
let mut state = serializer.serialize_struct("FriendlyRecord", 3)?;
|
#[derive(Serialize)]
|
||||||
state.serialize_field("ttl", &self.ttl)?;
|
struct ExtendedRecord<'a> {
|
||||||
state.serialize_field("data", &self.data)?;
|
ttl: i64,
|
||||||
state.serialize_field("rtype", &self.data.rtype())?;
|
record_type: FriendlyRType,
|
||||||
state.end()
|
record_section: RecordSection,
|
||||||
|
#[serde(flatten)]
|
||||||
|
data: &'a FriendlyRDataAggregated,
|
||||||
|
}
|
||||||
|
|
||||||
|
let extended_record = ExtendedRecord {
|
||||||
|
ttl: self.ttl,
|
||||||
|
data: &self.data,
|
||||||
|
record_type: self.record_type(),
|
||||||
|
record_section: self.record_section(),
|
||||||
|
};
|
||||||
|
|
||||||
|
extended_record.serialize(serializer)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FriendlyRecord<T> {
|
impl FriendlyRecord {
|
||||||
fn new(ttl: u32, data: T) -> Self {
|
pub fn new(ttl: u32, data: FriendlyRDataAggregated) -> Self {
|
||||||
FriendlyRecord {
|
FriendlyRecord {
|
||||||
ttl: ttl.into(),
|
ttl: ttl.into(),
|
||||||
data,
|
data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub fn record_type(&self) -> FriendlyRType {
|
||||||
pub struct FriendlyRecordSet<T> {
|
match self.data {
|
||||||
ttl: i64,
|
FriendlyRDataAggregated::Addresses(_) => FriendlyRType::Addresses,
|
||||||
data: Vec<T>,
|
FriendlyRDataAggregated::MailServers(_) => FriendlyRType::MailServers,
|
||||||
}
|
FriendlyRDataAggregated::NameServers(_) => FriendlyRType::NameServers,
|
||||||
|
FriendlyRDataAggregated::Service(_) => FriendlyRType::Service,
|
||||||
impl<T: Serialize + HasRType> Serialize for FriendlyRecordSet<T> {
|
FriendlyRDataAggregated::Spf(_) => FriendlyRType::Spf,
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
FriendlyRDataAggregated::Texts(_) => FriendlyRType::Texts,
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let mut state = serializer.serialize_struct("FriendlyRecordSet", 3)?;
|
|
||||||
state.serialize_field("ttl", &self.ttl)?;
|
|
||||||
state.serialize_field("data", &self.data)?;
|
|
||||||
state.serialize_field("rtype", &self.data.first().map(|d| d.rtype().to_string()))?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FriendlyRecordSet<T> {
|
|
||||||
fn new(ttl: u32) -> Self {
|
|
||||||
FriendlyRecordSet {
|
|
||||||
ttl: ttl.into(),
|
|
||||||
data: Vec::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
pub fn record_section(&self) -> RecordSection {
|
||||||
#[serde(rename_all="lowercase")]
|
match self.data {
|
||||||
pub enum ConfigurationType {
|
FriendlyRDataAggregated::Addresses(_) => RecordSection::Web,
|
||||||
Mail,
|
FriendlyRDataAggregated::MailServers(_) => RecordSection::Mail,
|
||||||
Web,
|
FriendlyRDataAggregated::NameServers(_) => RecordSection::Miscellaneous,
|
||||||
}
|
FriendlyRDataAggregated::Service(_) => RecordSection::Services,
|
||||||
|
FriendlyRDataAggregated::Spf(_) => RecordSection::Mail,
|
||||||
#[derive(Debug, Serialize)]
|
FriendlyRDataAggregated::Texts(_) => RecordSection::Miscellaneous,
|
||||||
pub struct MailConfiguration {
|
|
||||||
pub servers: Option<FriendlyRecordSet<MailServer>>,
|
|
||||||
pub spf: Option<FriendlyRecord<Spf>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MailConfiguration {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
MailConfiguration {
|
|
||||||
servers: None,
|
|
||||||
spf: None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct WebConfiguration {
|
pub struct Node {
|
||||||
pub addresses: Option<FriendlyRecordSet<Address>>,
|
pub name: String,
|
||||||
|
pub records: Vec<FriendlyRecord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
impl WebConfiguration {
|
fn new(name: String) -> Self {
|
||||||
pub fn new() -> Self {
|
Node {
|
||||||
WebConfiguration {
|
name,
|
||||||
addresses: None,
|
records: Vec::new(),
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct FriendlyRecordSetGroup {
|
|
||||||
owner: String,
|
|
||||||
general_records: Vec<FriendlyRecordSet<FriendlyRData>>,
|
|
||||||
#[serde(serialize_with = "as_vector")]
|
|
||||||
services: HashMap<ServiceType, FriendlyRecordSet<Service>>,
|
|
||||||
mail: Option<MailConfiguration>,
|
|
||||||
web: Option<WebConfiguration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_vector<S>(services: &HashMap<ServiceType, FriendlyRecordSet<Service>>, ser: S) -> Result<S::Ok, S::Error>
|
|
||||||
where S: Serializer
|
|
||||||
{
|
|
||||||
let container: Vec<_> = services.iter().collect();
|
|
||||||
serde::Serialize::serialize(&container, ser)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FriendlyRecordSetGroup {
|
|
||||||
pub fn new(owner: String) -> Self {
|
|
||||||
FriendlyRecordSetGroup {
|
|
||||||
owner,
|
|
||||||
general_records: Vec::new(),
|
|
||||||
services: HashMap::new(),
|
|
||||||
mail: None,
|
|
||||||
web: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct FriendlyRecords {
|
pub struct FriendlyRecords {
|
||||||
records: Vec<FriendlyRecordSetGroup>,
|
records: Vec<Node>,
|
||||||
aliases: Vec<Alias>,
|
aliases: Vec<Alias>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +145,8 @@ impl From<internal::RecordList> for FriendlyRecords {
|
||||||
fn from(value: internal::RecordList) -> Self {
|
fn from(value: internal::RecordList) -> Self {
|
||||||
|
|
||||||
let mut records = FriendlyRecords::new();
|
let mut records = FriendlyRecords::new();
|
||||||
|
let mut name_mapping: HashMap<String, HashMap<FriendlyRType, FriendlyRecord>> = HashMap::new();
|
||||||
|
let mut service_mapping: HashMap<(String, ServiceType), FriendlyRecord> = HashMap::new();
|
||||||
|
|
||||||
for record in value.records {
|
for record in value.records {
|
||||||
let internal::Record { name, ttl, rdata } = record;
|
let internal::Record { name, ttl, rdata } = record;
|
||||||
|
@ -216,100 +162,148 @@ impl From<internal::RecordList> for FriendlyRecords {
|
||||||
if let FriendlyRData::Alias(alias) = rdata {
|
if let FriendlyRData::Alias(alias) = rdata {
|
||||||
records.aliases.push(alias)
|
records.aliases.push(alias)
|
||||||
} else {
|
} else {
|
||||||
|
let node = name_mapping.entry(name.clone()).or_default();
|
||||||
if records.records.is_empty() {
|
|
||||||
records.records.push(FriendlyRecordSetGroup::new(name));
|
|
||||||
} else {
|
|
||||||
let group = records.records.last().unwrap();
|
|
||||||
|
|
||||||
if group.owner != name {
|
|
||||||
records.records.push(FriendlyRecordSetGroup::new(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let current_group = records.records.last_mut().unwrap();
|
|
||||||
|
|
||||||
match rdata {
|
match rdata {
|
||||||
FriendlyRData::Address(address) => {
|
FriendlyRData::Address(address) => {
|
||||||
let web = current_group.web.get_or_insert_with(WebConfiguration::new);
|
let addresses = node.entry(FriendlyRType::Addresses).or_insert_with(|| {
|
||||||
let addresses = web.addresses.get_or_insert_with(|| FriendlyRecordSet::new(ttl));
|
FriendlyRecord::new(ttl, FriendlyRDataAggregated::Addresses(Addresses {
|
||||||
|
addresses: Vec::new()
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
addresses.data.push(address);
|
match addresses.data {
|
||||||
|
FriendlyRDataAggregated::Addresses(ref mut addresses) => addresses.addresses.push(address),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
FriendlyRData::MailServer(mailserver) => {
|
FriendlyRData::MailServer(mailserver) => {
|
||||||
let mail: &mut MailConfiguration = current_group.mail.get_or_insert_with(MailConfiguration::new);
|
let mailservers = node.entry(FriendlyRType::MailServers).or_insert_with(|| {
|
||||||
let servers = mail.servers.get_or_insert_with(|| FriendlyRecordSet::new(ttl));
|
FriendlyRecord::new(ttl, FriendlyRDataAggregated::MailServers(MailServers {
|
||||||
|
mailservers: Vec::new()
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
servers.data.push(mailserver);
|
match mailservers.data {
|
||||||
|
FriendlyRDataAggregated::MailServers(ref mut mailservers) => mailservers.mailservers.push(mailserver),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
FriendlyRData::Spf(spf) => {
|
FriendlyRData::Spf(spf) => {
|
||||||
let mail: &mut MailConfiguration = current_group.mail.get_or_insert_with(MailConfiguration::new);
|
node.insert(FriendlyRType::Spf, FriendlyRecord::new(ttl, FriendlyRDataAggregated::Spf(spf)));
|
||||||
|
|
||||||
mail.spf = Some(FriendlyRecord::new(ttl, spf));
|
|
||||||
},
|
},
|
||||||
FriendlyRData::Service(service) => {
|
FriendlyRData::Service(service_single) => {
|
||||||
let services = current_group.services.entry(service.service_type.clone()).or_insert(FriendlyRecordSet::new(ttl));
|
let service = service_mapping.entry((name.clone(), service_single.service_type.clone()))
|
||||||
|
.or_insert_with(|| {
|
||||||
|
FriendlyRecord::new(ttl, FriendlyRDataAggregated::Service(Service {
|
||||||
|
service_type: service_single.service_type,
|
||||||
|
service_targets: Vec::new(),
|
||||||
|
|
||||||
services.data.push(service)
|
}))
|
||||||
}
|
});
|
||||||
FriendlyRData::Alias(_) => {},
|
|
||||||
data => {
|
match service.data {
|
||||||
|
FriendlyRDataAggregated::Service(ref mut service) => service.service_targets.push(service_single.service_target),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
FriendlyRData::NameServer(nameserver) => {
|
||||||
// TODO: NS -> Skip if NS for zone (authority), create Delegation section with glue + DS for others (how to check if record is glue?)
|
// TODO: NS -> Skip if NS for zone (authority), create Delegation section with glue + DS for others (how to check if record is glue?)
|
||||||
|
let nameservers = node.entry(FriendlyRType::NameServers).or_insert_with(|| {
|
||||||
|
FriendlyRecord::new(ttl, FriendlyRDataAggregated::NameServers(NameServers {
|
||||||
|
nameservers: Vec::new()
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
if current_group.general_records.is_empty() {
|
match nameservers.data {
|
||||||
current_group.general_records.push(FriendlyRecordSet::new(ttl));
|
FriendlyRDataAggregated::NameServers(ref mut nameservers) => nameservers.nameservers.push(nameserver),
|
||||||
} else {
|
_ => unreachable!(),
|
||||||
let rrset = current_group.general_records.last().unwrap();
|
};
|
||||||
if rrset.data.last().unwrap().rtype() != data.rtype() {
|
},
|
||||||
current_group.general_records.push(FriendlyRecordSet::new(ttl));
|
FriendlyRData::TextData(text) => {
|
||||||
}
|
let texts = node.entry(FriendlyRType::Texts).or_insert_with(|| {
|
||||||
}
|
FriendlyRecord::new(ttl, FriendlyRDataAggregated::Texts(Texts {
|
||||||
|
texts: Vec::new()
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
let current_rrset = current_group.general_records.last_mut().unwrap();
|
match texts.data {
|
||||||
current_rrset.data.push(data)
|
FriendlyRDataAggregated::Texts(ref mut texts) => texts.texts.push(text),
|
||||||
}
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
FriendlyRData::Alias(_) => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut nodes: HashMap<String, Node> = HashMap::new();
|
||||||
|
|
||||||
|
for ((name, _), service) in service_mapping {
|
||||||
|
let node = nodes.entry(name.clone())
|
||||||
|
.or_insert_with(|| Node::new(name));
|
||||||
|
node.records.push(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name, node_records) in name_mapping {
|
||||||
|
let node = nodes.entry(name.clone())
|
||||||
|
.or_insert_with(|| Node::new(name));
|
||||||
|
for (_, record) in node_records {
|
||||||
|
node.records.push(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
records.records = nodes.into_values().collect();
|
||||||
|
|
||||||
|
records.records.sort_by_key(|node| node.name.clone());
|
||||||
|
|
||||||
records
|
records
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------- RDATA --------- */
|
/* --------- RDATA --------- */
|
||||||
|
|
||||||
|
/* --------- Address --------- */
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
pub address: String
|
pub address: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Addresses {
|
||||||
|
pub addresses: Vec<Address>,
|
||||||
|
}
|
||||||
|
|
||||||
impl HasRType for Address {
|
/* --------- Service --------- */
|
||||||
fn rtype(&self) -> FriendlyRType {
|
|
||||||
FriendlyRType::Address
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
}
|
pub struct ServiceSingleTarget {
|
||||||
|
pub service_type: ServiceType,
|
||||||
|
pub service_target: ServiceTarget,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
pub service_type: ServiceType,
|
pub service_type: ServiceType,
|
||||||
|
pub service_targets: Vec<ServiceTarget>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct ServiceTarget {
|
||||||
pub port: i64,
|
pub port: i64,
|
||||||
pub weight: i64,
|
pub weight: i64,
|
||||||
pub priority: i64,
|
pub priority: i64,
|
||||||
pub server: String,
|
pub server: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Hash, Eq, PartialEq, Clone)]
|
#[derive(Debug, Hash, Eq, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "lowercase", tag = "service_type")]
|
#[serde(rename_all = "lowercase", tag = "service_type")]
|
||||||
pub enum ServiceType {
|
pub enum ServiceType {
|
||||||
Other { protocol: String, name: String },
|
Other { protocol: String, name: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasRType for Service {
|
/* --------- MailServer --------- */
|
||||||
fn rtype(&self) -> FriendlyRType {
|
|
||||||
FriendlyRType::Service
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct MailServer {
|
pub struct MailServer {
|
||||||
|
@ -317,36 +311,47 @@ pub struct MailServer {
|
||||||
pub mail_exchanger: String,
|
pub mail_exchanger: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasRType for MailServer {
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
fn rtype(&self) -> FriendlyRType {
|
pub struct MailServers {
|
||||||
FriendlyRType::MailServer
|
pub mailservers: Vec<MailServer>
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------- NameServer --------- */
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct NameServer {
|
pub struct NameServer {
|
||||||
pub target: String,
|
pub target: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct NameServers {
|
||||||
|
pub nameservers: Vec<NameServer>
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------- TextData --------- */
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct TextData {
|
pub struct TextData {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Texts {
|
||||||
|
pub texts: Vec<TextData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------- Spf --------- */
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Spf {
|
||||||
|
pub policy: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------- Alias --------- */
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Alias {
|
pub struct Alias {
|
||||||
pub from: String,
|
pub from: String,
|
||||||
pub target: String,
|
pub target: String,
|
||||||
pub ttl: i64,
|
pub ttl: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct Spf {
|
|
||||||
pub policy: String
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasRType for Spf {
|
|
||||||
fn rtype(&self) -> FriendlyRType {
|
|
||||||
FriendlyRType::Spf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -147,15 +147,17 @@ 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((labels[2].to_string(), friendly::FriendlyRData::Service(friendly::Service {
|
Some((labels[2].to_string(), friendly::FriendlyRData::Service(friendly::ServiceSingleTarget {
|
||||||
service_type: friendly::ServiceType::Other {
|
service_type: friendly::ServiceType::Other {
|
||||||
name: service_name.into(),
|
name: service_name.into(),
|
||||||
protocol: protocol.into()
|
protocol: protocol.into()
|
||||||
},
|
},
|
||||||
port: self.port.into(),
|
service_target: friendly::ServiceTarget {
|
||||||
weight: self.weight.into(),
|
port: self.port.into(),
|
||||||
priority: self.priority.into(),
|
weight: self.weight.into(),
|
||||||
server: self.server.to_string(),
|
priority: self.priority.into(),
|
||||||
|
server: self.server.to_string()
|
||||||
|
}
|
||||||
})))
|
})))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use axum::extract::{Query, Path, State, OriginalUri};
|
use axum::extract::{Query, Path, State, OriginalUri};
|
||||||
use axum::Extension;
|
use axum::Extension;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use unic_langid::LanguageIdentifier;
|
use unic_langid::LanguageIdentifier;
|
||||||
|
|
||||||
|
@ -35,10 +35,17 @@ pub async fn get_records_page(
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct NewRecordQuery {
|
pub struct NewRecordQuery {
|
||||||
subdomain: Option<String>,
|
subdomain: Option<String>,
|
||||||
config: Option<friendly::ConfigurationType>,
|
config: Option<ConfigurationType>,
|
||||||
rtype: Option<friendly::FriendlyRType>,
|
rtype: Option<friendly::FriendlyRType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum ConfigurationType {
|
||||||
|
Mail,
|
||||||
|
Web,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_new_record_page(
|
pub async fn get_new_record_page(
|
||||||
Path(zone_name): Path<String>,
|
Path(zone_name): Path<String>,
|
||||||
State(app): State<AppState>,
|
State(app): State<AppState>,
|
||||||
|
|
|
@ -1,81 +1,93 @@
|
||||||
{% macro rrset(rtype, ttl, data, zone, lang, service_type="") %}
|
{% macro rrset(record, zone, lang) %}
|
||||||
<li class="rrset">
|
<li class="rrset">
|
||||||
<div class="rtype">
|
<div class="rtype">
|
||||||
{% if rtype == "service" %}
|
{% if record.record_type == "service" %}
|
||||||
{% if service_type.service_type == "other" %}
|
{% if record.service.service_type.service_type == "other" %}
|
||||||
{{ service_type.name }}/{{ service_type.protocol }}
|
{{ record.service.service_type.name }}/{{ record.service.service_type.protocol }}
|
||||||
{% else %}
|
|
||||||
{{ service_type.service_type }}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ tr(msg="zone-content-record-type-" ~ rtype, attr="type-name", lang=lang) }}
|
{{ record.srvice.service_type.service_type }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="action">
|
{% else %}
|
||||||
<a class="button icon" href="#">
|
{{ tr(msg="zone-content-record-type-" ~ record.record_type, attr="type-name", lang=lang) }}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">
|
{% endif %}
|
||||||
<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"/>
|
<div class="action">
|
||||||
</svg>
|
<a class="button icon" href="#">
|
||||||
</a>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">
|
||||||
</div>
|
<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"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{% for data in data %}
|
{% if record.record_type == "addresses" %}
|
||||||
|
{% for address in record.addresses.addresses %}
|
||||||
<li>
|
<li>
|
||||||
<div class="rdata">
|
<div class="rdata">
|
||||||
{% if rtype == "address" %}
|
|
||||||
<div class="rdata-main">
|
<div class="rdata-main">
|
||||||
<span class="pill">
|
<span class="pill">
|
||||||
{{ data.address }}
|
{{ address.address }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% elif rtype == "mailserver" %}
|
|
||||||
<div class="rdata-main">
|
|
||||||
<span class="pill">
|
|
||||||
{{ data.mail_exchanger }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="rdata-complementary">
|
|
||||||
<span class="pill">
|
|
||||||
{{ tr(
|
|
||||||
msg="zone-content-record-type-mailserver",
|
|
||||||
attr="data-preference",
|
|
||||||
preference=data.preference,
|
|
||||||
lang=lang) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{% elif rtype == "nameserver" %}
|
|
||||||
<div class="rdata-main">
|
|
||||||
<span class="pill">
|
|
||||||
{{ data.target }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{% elif rtype == "service" %}
|
|
||||||
<div class="rdata-main">
|
|
||||||
<span class="pill">
|
|
||||||
{{ data.server ~ ":" ~ data.port }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="rdata-complementary">
|
|
||||||
<span class="pill">
|
|
||||||
{{ tr(
|
|
||||||
msg="zone-content-record-type-service",
|
|
||||||
attr="data-priority",
|
|
||||||
priority=data.priority,
|
|
||||||
lang=lang) }}
|
|
||||||
</span>
|
|
||||||
<span class="pill">
|
|
||||||
{{ tr(
|
|
||||||
msg="zone-content-record-type-service",
|
|
||||||
attr="data-weight",
|
|
||||||
weight=data.weight,
|
|
||||||
lang=lang) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
{% elif record.record_type == "mailservers" %}
|
||||||
</li>
|
{% for mailserver in record.mailservers.mailservers %}
|
||||||
|
<li>
|
||||||
|
<div class="rdata-main">
|
||||||
|
<span class="pill">
|
||||||
|
{{ mailserver.mail_exchanger }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="rdata-complementary">
|
||||||
|
<span class="pill">
|
||||||
|
{{ tr(
|
||||||
|
msg="zone-content-record-type-mailservers",
|
||||||
|
attr="data-preference",
|
||||||
|
preference=mailserver.preference,
|
||||||
|
lang=lang) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% elif record.record_type == "nameservers" %}
|
||||||
|
{% for nameserver in record.nameservers.nameservers %}
|
||||||
|
<li>
|
||||||
|
<div class="rdata-main">
|
||||||
|
<span class="pill">
|
||||||
|
{{ nameserver.target }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% elif record.record_type == "service" %}
|
||||||
|
{% for service_target in record.service.service_targets %}
|
||||||
|
<li>
|
||||||
|
<div class="rdata-main">
|
||||||
|
<span class="pill">
|
||||||
|
{{ service_target.server ~ ":" ~ service_target.port }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="rdata-complementary">
|
||||||
|
<span class="pill">
|
||||||
|
{{ tr(
|
||||||
|
msg="zone-content-record-type-service",
|
||||||
|
attr="data-priority",
|
||||||
|
priority=service_target.priority,
|
||||||
|
lang=lang) }}
|
||||||
|
</span>
|
||||||
|
<span class="pill">
|
||||||
|
{{ tr(
|
||||||
|
msg="zone-content-record-type-service",
|
||||||
|
attr="data-weight",
|
||||||
|
weight=service_target.weight,
|
||||||
|
lang=lang) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
{% endmacro rrset %}
|
{% endmacro rrset %}
|
||||||
|
|
|
@ -4,131 +4,13 @@
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<h1>Create a new record in zone {{ current_zone }}</h1>
|
<h1>Create a new record in zone {{ current_zone }}</h1>
|
||||||
|
|
||||||
{% if not new_record_name or (new_record_name and domain_error) %}
|
{% if not new_record_name or (new_record_name and domain_error) %}
|
||||||
<h2>Choose the name of the new record</h2>
|
{% include "pages/new_record/choose_name.html" %}
|
||||||
<form action="" method="GET">
|
|
||||||
<label for="subdomain">Name of the new record</label>
|
|
||||||
<div class="input-group">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="subdomain"
|
|
||||||
id="subdomain"
|
|
||||||
{% if domain_error %}aria-invalid="true"{% endif %}
|
|
||||||
aria-describedby="subdomain-help{% if domain_error %} subdomain-error{% endif %}"
|
|
||||||
>
|
|
||||||
<span>.{{ current_zone }}</span>
|
|
||||||
</div>
|
|
||||||
{% if domain_error %}
|
|
||||||
<p class="error" id="subdomain-error">{{ domain_error.description }}</p>
|
|
||||||
{% endif %}
|
|
||||||
<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>
|
|
||||||
</form>
|
|
||||||
{% elif not config and not rtype %}
|
{% elif not config and not rtype %}
|
||||||
|
{% include "pages/new_record/choose_record.html" %}
|
||||||
<h2>Configure the domain {{ new_record_name }}...</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{{ url }}&config=web">Web site</a></li>
|
|
||||||
<li><a href="{{ url }}&config=mail">E-mails</a></li>
|
|
||||||
</ul>
|
|
||||||
<h2>...or create a new record for the domain {{ new_record_name }}</h2>
|
|
||||||
<h3>General</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{{ url }}&rtype=address">Address (A or AAAA)</a></li>
|
|
||||||
<li><a href="{{ url }}&rtype=alias">Alias (CNAME)</a></li>
|
|
||||||
<li><a href="{{ url }}&rtype=text">Text (TXT)</a></li>
|
|
||||||
<li><a href="{{ url }}&rtype=service">Service (SRV)</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>E-mails</h3>
|
|
||||||
<ul>sdv
|
|
||||||
<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=dkim">Cryptographic signature (DKIM)</a></li>
|
|
||||||
<li><a href="{{ url }}&rtype=dmarc">Error reporting (DMARC)</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Security</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{{ url }}&rtype=dane">Domain authentication (TLSA)</a></li>
|
|
||||||
<li><a href="{{ url }}&rtype=sshfp">SSH keys fingerprint (SSHFP)</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>DNS Delegation</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{{ url }}&rtype=nameserver">Nameserver (NS)</a></li>
|
|
||||||
<li><a href="{{ url }}&rtype=ds">Delegation signer (DS)</a></li>
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
{% include "pages/new_record/configure_record.html" %}
|
||||||
{% 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 %}
|
||||||
|
|
19
templates/pages/new_record/choose_name.html
Normal file
19
templates/pages/new_record/choose_name.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<h2>Choose the name of the new record</h2>
|
||||||
|
<form action="" method="GET">
|
||||||
|
<label for="subdomain">Name of the new record</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="subdomain"
|
||||||
|
id="subdomain"
|
||||||
|
{% if domain_error %}aria-invalid="true"{% endif %}
|
||||||
|
aria-describedby="subdomain-help{% if domain_error %} subdomain-error{% endif %}"
|
||||||
|
>
|
||||||
|
<span>.{{ current_zone }}</span>
|
||||||
|
</div>
|
||||||
|
{% if domain_error %}
|
||||||
|
<p class="error" id="subdomain-error">{{ domain_error.description }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<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>
|
||||||
|
</form>
|
30
templates/pages/new_record/choose_record.html
Normal file
30
templates/pages/new_record/choose_record.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<h2>Configure the domain {{ new_record_name }}...</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url }}&config=web">Web site</a></li>
|
||||||
|
<li><a href="{{ url }}&config=mail">E-mails</a></li>
|
||||||
|
</ul>
|
||||||
|
<h2>...or create a new record for the domain {{ new_record_name }}</h2>
|
||||||
|
<h3>General</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url }}&rtype=address">Address (A or AAAA)</a></li>
|
||||||
|
<li><a href="{{ url }}&rtype=alias">Alias (CNAME)</a></li>
|
||||||
|
<li><a href="{{ url }}&rtype=text">Text (TXT)</a></li>
|
||||||
|
<li><a href="{{ url }}&rtype=service">Service (SRV)</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>E-mails</h3>
|
||||||
|
<ul>sdv
|
||||||
|
<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=dkim">Cryptographic signature (DKIM)</a></li>
|
||||||
|
<li><a href="{{ url }}&rtype=dmarc">Error reporting (DMARC)</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>Security</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url }}&rtype=dane">Domain authentication (TLSA)</a></li>
|
||||||
|
<li><a href="{{ url }}&rtype=sshfp">SSH keys fingerprint (SSHFP)</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>DNS Delegation</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url }}&rtype=nameserver">Nameserver (NS)</a></li>
|
||||||
|
<li><a href="{{ url }}&rtype=ds">Delegation signer (DS)</a></li>
|
||||||
|
</ul>
|
68
templates/pages/new_record/configure_record.html
Normal file
68
templates/pages/new_record/configure_record.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{% 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="record-0-addresses-address-0">IP Address #1</label>
|
||||||
|
<input name="records[0][addresses][addresses][0][address]" id="record-0-addresses-address-0" 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 %}
|
|
@ -12,14 +12,15 @@
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>{{ tr(msg="zone-content-records-header", lang=lang) }}</h2>
|
<h2>{{ tr(msg="zone-content-records-header", lang=lang) }}</h2>
|
||||||
{% for group in records.records %}
|
{% for node in records.records %}
|
||||||
<article class="domain">
|
<article class="domain">
|
||||||
<header>
|
<header>
|
||||||
<h3 class="folder-tab">{{ group.owner }}</h3>
|
<h3 class="folder-tab">{{ node.name }}</h3>
|
||||||
<span class="sep"></span>
|
<span class="sep"></span>
|
||||||
<a href="{{ url }}/new?subdomain={{ group.owner | trim_end_matches(pat=current_zone) | trim_end_matches(pat=".") }}" class="button">
|
<a href="{{ url }}/new?subdomain={{ node.name | trim_end_matches(pat=current_zone) | trim_end_matches(pat=".") }}" class="button">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-circle" viewBox="0 0 16 16" aria-hidden="true">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-circle" viewBox="0 0 16 16" aria-hidden="true">
|
||||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
|
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
|
||||||
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4"/>
|
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4"/>
|
||||||
|
@ -27,66 +28,58 @@
|
||||||
{{ tr(msg="zone-content-new-record-button", lang=lang) }}
|
{{ tr(msg="zone-content-new-record-button", lang=lang) }}
|
||||||
</a>
|
</a>
|
||||||
</header>
|
</header>
|
||||||
|
{% set sections = node.records | group_by(attribute="record_section") %}
|
||||||
<div class="records">
|
<div class="records">
|
||||||
{% if group.web %}
|
{% if sections.web %}
|
||||||
|
{% set records = sections.web | group_by(attribute="record_type") %}
|
||||||
<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 records.addresses %}
|
||||||
{{ rrset::rrset(
|
{{ rrset::rrset(
|
||||||
rtype=group.web.addresses.rtype,
|
record=records.addresses.0,
|
||||||
ttl=group.web.addresses.ttl,
|
|
||||||
data=group.web.addresses.data,
|
|
||||||
zone=current_zone,
|
zone=current_zone,
|
||||||
lang=lang) }}
|
lang=lang) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if group.mail %}
|
{% if sections.mail %}
|
||||||
|
{% set records = sections.mail | group_by(attribute="record_type") %}
|
||||||
<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 records.mailservers %}
|
||||||
{{ rrset::rrset(
|
{{ rrset::rrset(
|
||||||
rtype=group.mail.servers.rtype,
|
record=records.mailservers.0,
|
||||||
ttl=group.mail.servers.ttl,
|
|
||||||
data=group.mail.servers.data,
|
|
||||||
zone=current_zone,
|
zone=current_zone,
|
||||||
lang=lang) }}
|
lang=lang) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if group.mail.spf %}
|
{% if records.spf %}
|
||||||
{{ rrset::rrset(
|
{{ rrset::rrset(
|
||||||
rtype=group.mail.spf.rtype,
|
record=records.spf.0,
|
||||||
ttl=group.mail.spf.ttl,
|
|
||||||
data=group.mail.spf.data,
|
|
||||||
zone=current_zone,
|
zone=current_zone,
|
||||||
lang=lang) }}
|
lang=lang) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if group.services %}
|
{% if sections.services %}
|
||||||
<h4>{{ tr(msg="zone-content-section-services-header", lang=lang) }}</h4>
|
<h4>{{ tr(msg="zone-content-section-services-header", lang=lang) }}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
{% for service in group.services %}
|
{% for service in sections.services %}
|
||||||
{{ rrset::rrset(
|
{{ rrset::rrset(
|
||||||
rtype=service[1].rtype,
|
record=service,
|
||||||
ttl=service[1].ttl,
|
|
||||||
data=service[1].data,
|
|
||||||
zone=current_zone,
|
zone=current_zone,
|
||||||
lang=lang,
|
lang=lang) }}
|
||||||
service_type=service[0]) }}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if group.general_records %}
|
{% if sections.miscellaneous %}
|
||||||
<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 record in sections.miscellaneous %}
|
||||||
{{ rrset::rrset(
|
{{ rrset::rrset(
|
||||||
rtype=rrset.rtype,
|
record=record,
|
||||||
ttl=rrset.ttl,
|
|
||||||
data=rrset.data,
|
|
||||||
zone=current_zone,
|
zone=current_zone,
|
||||||
lang=lang) }}
|
lang=lang) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
Loading…
Reference in a new issue