add flash message on zone configuration success

This commit is contained in:
Hannaeko 2025-09-30 13:57:47 +02:00
parent 6dd59d7a3a
commit d8c2f1f164
10 changed files with 393 additions and 98 deletions

263
Cargo.lock generated
View file

@ -17,6 +17,41 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "aes-gcm"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "ahash"
version = "0.8.11"
@ -68,9 +103,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
version = "0.8.1"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
checksum = "98e529aee37b5c8206bb4bf4c44797127566d72f76952c970bd3d1e85de8f4e2"
dependencies = [
"axum-core",
"bytes",
@ -87,8 +122,7 @@ dependencies = [
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
@ -101,11 +135,32 @@ dependencies = [
[[package]]
name = "axum-core"
version = "0.5.0"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
checksum = "0ac7a6beb1182c7e30253ee75c3e918080bfb83f5a3023bcdf7209d85fd147e6"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"sync_wrapper",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-extra"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86d701cd16f401888ebe9c3214dc838c7ef27a405d5726196765a913603b5dd"
dependencies = [
"axum",
"axum-core",
"bytes",
"cookie",
"futures-util",
"http",
"http-body",
@ -113,7 +168,7 @@ dependencies = [
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper",
"serde_core",
"tower-layer",
"tower-service",
]
@ -133,6 +188,12 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bb8"
version = "0.9.0"
@ -202,6 +263,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@ -211,6 +282,23 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "cookie"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [
"aes-gcm",
"base64",
"hkdf",
"percent-encoding",
"rand",
"sha2",
"subtle",
"time",
"version_check",
]
[[package]]
name = "cpufeatures"
version = "0.2.16"
@ -261,9 +349,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"rand_core",
"typenum",
]
[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -281,6 +379,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -467,6 +566,16 @@ dependencies = [
"wasi",
]
[[package]]
name = "ghash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "gimli"
version = "0.31.1"
@ -515,6 +624,24 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "hkdf"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [
"hmac",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "http"
version = "1.2.0"
@ -618,6 +745,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "intl-memoizer"
version = "0.5.2"
@ -637,6 +773,17 @@ dependencies = [
"unic-langid",
]
[[package]]
name = "io-uring"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "itoa"
version = "1.0.14"
@ -661,9 +808,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.168"
version = "0.2.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
[[package]]
name = "libsqlite3-sys"
@ -736,7 +883,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -769,6 +916,7 @@ version = "0.2.0-dev"
dependencies = [
"async-trait",
"axum",
"axum-extra",
"bb8",
"domain",
"fluent-bundle",
@ -814,6 +962,12 @@ version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "parking"
version = "2.2.1"
@ -912,6 +1066,18 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "polyval"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
@ -1049,7 +1215,7 @@ dependencies = [
"libc",
"spin",
"untrusted",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -1137,18 +1303,28 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.215"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.215"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@ -1223,12 +1399,12 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.8"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@ -1237,6 +1413,12 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.90"
@ -1323,10 +1505,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
@ -1335,6 +1519,16 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinystr"
version = "0.7.6"
@ -1346,26 +1540,28 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.42.0"
version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"slab",
"socket2",
"tokio-macros",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "tokio-macros"
version = "2.4.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
@ -1575,6 +1771,16 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.9.0"
@ -1704,7 +1910,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -1722,6 +1928,15 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"

View file

@ -19,8 +19,8 @@ tokio = {version = "1", default-features = false, features = [ "macros", "rt-mul
#argon2 = { version = "0.5", default-features = false, features = ["alloc", "password-hash"] }
#rand = "0.8"
tera = { version = "1", default-features = false }
domain = { version = "0.10.3", features = [ "tsig", "unstable-client-transport" ]}
axum = { version = "0.8.1", default-features = false, features = [ "http1", "json", "form", "query", "tokio", "original-uri" ]}
domain = { version = "0.10", features = [ "tsig", "unstable-client-transport" ]}
axum = { version = "0.8", default-features = false, features = [ "http1", "json", "form", "query", "tokio", "original-uri" ]}
bb8 = { version = "0.9" }
rusqlite = { version = "0.32"}
async-trait = { version = "0.1" }
@ -29,3 +29,4 @@ fluent-bundle = "0.15.3"
unic-langid = "*"
tower = "*"
serde_urlencoded = "*"
axum-extra = { version = "0.10", default-features = false, features = [ "cookie-private", "cookie-key-expansion" ] }

View file

@ -32,6 +32,15 @@ h2 {
font-weight: 300;
}
.flash {
padding: 1rem;
border-radius: .3rem;
&.success {
background: #b9f2bf;
}
}
article.domain {
margin-bottom: 2em;

View file

@ -41,6 +41,9 @@ zone-content-record-type-texts =
zone-content-new-record-button = New record
zone-content-flash-message =
.configure-success = Zone configured successfully.
## Create record
new-record-title = New record

View file

@ -41,6 +41,9 @@ zone-content-record-type-service =
zone-content-new-record-button = Nouvel enregistrement
zone-content-flash-message =
.configure-success = Zone configurée avec succès.
## Create record
new-record-title = Nouvel enregistrement

View file

@ -3,20 +3,20 @@ use std::collections::HashMap;
use axum::extract::{Request, FromRequest};
use axum::response::{Response, IntoResponse};
use axum::http::StatusCode;
use axum::Form;
use axum::Form as AxumForm;
impl<S> FromRequest<S> for Node
impl<S> FromRequest<S> for Form
where
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let Form(data): Form<Vec<(String, String)>> = Form::from_request(req, state)
let AxumForm(data): AxumForm<Vec<(String, String)>> = AxumForm::from_request(req, state)
.await
.map_err(IntoResponse::into_response)?;
let node = Node::from_key_value(data)
let node = Form::from_key_value(data)
.map_err(|_| StatusCode::UNPROCESSABLE_ENTITY.into_response())?;
Ok(node)
@ -30,15 +30,15 @@ pub enum FormError {
}
#[derive(Debug, PartialEq, Eq)]
pub enum Node {
pub enum Form {
Value(String),
Map(HashMap<String, Node>),
Map(HashMap<String, Form>),
Sequence(Sequence)
}
impl Node {
pub fn from_key_value(data: Vec<(String, String)>) -> Result<Node, FormError> {
let mut form = Node::Map(HashMap::new());
impl Form {
pub fn from_key_value(data: Vec<(String, String)>) -> Result<Form, FormError> {
let mut form = Form::Map(HashMap::new());
for (key, value) in data {
// Consider empty value not filled and remove them
@ -54,14 +54,14 @@ impl Node {
let child_key = &window[1];
parent_node = match (parent_node, parent_key) {
(&mut Node::Map(ref mut map), Key::Attribute(key)) => {
(&mut Form::Map(ref mut map), Key::Attribute(key)) => {
map.entry(key.clone()).or_insert_with(|| child_key.new_node())
},
(&mut Node::Sequence(Sequence::ImplicitIndex(ref mut list)), Key::ImplicitIndex) => {
(&mut Form::Sequence(Sequence::ImplicitIndex(ref mut list)), Key::ImplicitIndex) => {
list.push(child_key.new_node());
list.last_mut().unwrap()
},
(&mut Node::Sequence(Sequence::ExplicitIndex(ref mut list)), Key::ExplicitIndex(index)) => {
(&mut Form::Sequence(Sequence::ExplicitIndex(ref mut list)), Key::ExplicitIndex(index)) => {
list.entry(*index).or_insert_with(|| child_key.new_node())
},
_ => {
@ -72,14 +72,14 @@ impl Node {
let last_key = path.last().unwrap();
match (parent_node, last_key) {
(&mut Node::Map(ref mut map), Key::Attribute(key)) => {
map.insert(key.clone(), Node::Value(value));
(&mut Form::Map(ref mut map), Key::Attribute(key)) => {
map.insert(key.clone(), Form::Value(value));
},
(&mut Node::Sequence(Sequence::ImplicitIndex(ref mut list)), Key::ImplicitIndex) => {
list.push(Node::Value(value))
(&mut Form::Sequence(Sequence::ImplicitIndex(ref mut list)), Key::ImplicitIndex) => {
list.push(Form::Value(value))
},
(&mut Node::Sequence(Sequence::ExplicitIndex(ref mut list)), Key::ExplicitIndex(index)) => {
list.insert(*index, Node::Value(value));
(&mut Form::Sequence(Sequence::ExplicitIndex(ref mut list)), Key::ExplicitIndex(index)) => {
list.insert(*index, Form::Value(value));
},
_ => {
return Err(FormError::MismatchedType);
@ -113,14 +113,14 @@ impl Node {
pub fn into_json_value(self) -> serde_json::Value {
match self {
Node::Value(value) => serde_json::Value::String(value),
Node::Map(map) => {
Form::Value(value) => serde_json::Value::String(value),
Form::Map(map) => {
let map = map.into_iter()
.map(|(key, node)| (key, node.into_json_value()))
.collect();
serde_json::Value::Object(map)
},
Node::Sequence(list) => {
Form::Sequence(list) => {
let array = list.into_vec()
.into_iter()
.map(|node| node.into_json_value())
@ -134,16 +134,16 @@ impl Node {
#[derive(Debug, PartialEq, Eq)]
pub enum Sequence {
ImplicitIndex(Vec<Node>),
ExplicitIndex(HashMap<usize, Node>),
ImplicitIndex(Vec<Form>),
ExplicitIndex(HashMap<usize, Form>),
}
impl Sequence {
pub fn into_vec(self) -> Vec<Node> {
pub fn into_vec(self) -> Vec<Form> {
match self {
Sequence::ImplicitIndex(list) => list,
Sequence::ExplicitIndex(map) => {
let mut key_values: Vec<(usize, Node)> = map.into_iter().collect();
let mut key_values: Vec<(usize, Form)> = map.into_iter().collect();
key_values.sort_by_key(|(k, _)| *k);
key_values.into_iter().map(|(_, v)| v).collect()
}
@ -159,11 +159,11 @@ pub enum Key {
}
impl Key {
pub fn new_node(&self) -> Node {
pub fn new_node(&self) -> Form {
match self {
Key::ExplicitIndex(_) => Node::Sequence(Sequence::ExplicitIndex(HashMap::new())),
Key::ImplicitIndex => Node::Sequence(Sequence::ImplicitIndex(Vec::new())),
Key::Attribute(_) => Node::Map(HashMap::new()),
Key::ExplicitIndex(_) => Form::Sequence(Sequence::ExplicitIndex(HashMap::new())),
Key::ImplicitIndex => Form::Sequence(Sequence::ImplicitIndex(Vec::new())),
Key::Attribute(_) => Form::Map(HashMap::new()),
}
}
}
@ -184,7 +184,7 @@ mod tests {
Key::ImplicitIndex,
Key::Attribute("address".to_string()),
];
assert_eq!(Node::parse_key(&key), parsed_key);
assert_eq!(Form::parse_key(&key), parsed_key);
}
#[test]
@ -194,45 +194,45 @@ mod tests {
("records[0][addresses][][address]".to_string(), "abc".to_string()),
];
let mut address1 = HashMap::new();
address1.insert("address".to_string(), Node::Value("123".to_string()));
address1.insert("address".to_string(), Form::Value("123".to_string()));
let mut address2 = HashMap::new();
address2.insert("address".to_string(), Node::Value("abc".to_string()));
address2.insert("address".to_string(), Form::Value("abc".to_string()));
let addresses = vec![Node::Map(address1), Node::Map(address2)];
let addresses = vec![Form::Map(address1), Form::Map(address2)];
let mut record = HashMap::new();
record.insert("addresses".to_string(), Node::Sequence(Sequence::ImplicitIndex(addresses)));
record.insert("addresses".to_string(), Form::Sequence(Sequence::ImplicitIndex(addresses)));
let mut record_list = HashMap::new();
record_list.insert(0, Node::Map(record));
record_list.insert(0, Form::Map(record));
let mut form = HashMap::new();
form.insert("records".to_string(), Node::Sequence(Sequence::ExplicitIndex(record_list)));
form.insert("records".to_string(), Form::Sequence(Sequence::ExplicitIndex(record_list)));
let parsed_form = Node::Map(form);
let parsed_form = Form::Map(form);
assert_eq!(Node::from_key_value(form_data).unwrap(), parsed_form);
assert_eq!(Form::from_key_value(form_data).unwrap(), parsed_form);
}
#[test]
pub fn test_json_value() {
let mut address1 = HashMap::new();
address1.insert("address".to_string(), Node::Value("123".to_string()));
address1.insert("address".to_string(), Form::Value("123".to_string()));
let mut address2 = HashMap::new();
address2.insert("address".to_string(), Node::Value("abc".to_string()));
address2.insert("address".to_string(), Form::Value("abc".to_string()));
let addresses = vec![Node::Map(address1), Node::Map(address2)];
let addresses = vec![Form::Map(address1), Form::Map(address2)];
let mut record = HashMap::new();
record.insert("addresses".to_string(), Node::Sequence(Sequence::ImplicitIndex(addresses)));
record.insert("addresses".to_string(), Form::Sequence(Sequence::ImplicitIndex(addresses)));
let mut record_list = HashMap::new();
record_list.insert(0, Node::Map(record));
record_list.insert(0, Form::Map(record));
let mut form = HashMap::new();
form.insert("records".to_string(), Node::Sequence(Sequence::ExplicitIndex(record_list)));
form.insert("records".to_string(), Form::Sequence(Sequence::ExplicitIndex(record_list)));
let parsed_form = Node::Map(form);
let parsed_form = Form::Map(form);
let json_value = json!({
"records": [

View file

@ -12,9 +12,11 @@ mod form;
use std::sync::Arc;
use axum::extract::FromRef;
use axum::Router;
use axum::routing;
use tower_http::services::ServeDir;
use axum_extra::extract::cookie::Key;
use database::sqlite::SqliteDB;
use database::BoxedDb;
@ -29,7 +31,14 @@ pub struct AppState {
zone: Arc<dyn ZoneDriver>,
records: Arc<dyn RecordDriver>,
db: BoxedDb,
template_engine: TemplateEngine
template_engine: TemplateEngine,
key: Key,
}
impl FromRef<AppState> for Key {
fn from_ref(state: &AppState) -> Self {
state.key.clone()
}
}
#[tokio::main]
@ -56,7 +65,8 @@ async fn main() {
zone: dns_driver.clone(),
records: dns_driver.clone(),
db: Arc::new(SqliteDB::new("db.sqlite".into()).await),
template_engine
template_engine,
key: Key::derive_from(b"NdId2vPhH8H7L6e4hzissFTCV1/uamf1k5NFl+ZewJ8="),
};
let app = Router::new()

View file

@ -1,11 +1,15 @@
use std::time::Duration;
use axum::extract::{Query, Path, State, OriginalUri};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::response::{IntoResponse, Redirect, Response};
use axum::Extension;
use axum_extra::extract::cookie::{self, Cookie};
use axum_extra::extract::PrivateCookieJar;
use serde_json::{Value, json};
use unic_langid::LanguageIdentifier;
use crate::form::Node;
use crate::form::Form;
use crate::macros::append_errors;
use crate::AppState;
use crate::errors::{Error, error_map};
@ -13,26 +17,55 @@ use crate::template::Template;
use crate::resources::dns::friendly::{self, NewRecordQuery};
use crate::resources::dns::internal;
const COOKIE_FLASH: &str = "flash";
// TODO: move to utils module
fn make_cookie(key: String, value: String) -> Cookie<'static> {
Cookie::build((key, value))
.secure(true)
.http_only(true)
.same_site(cookie::SameSite::Strict)
// TODO: Configure path to base url path
.path("/")
.max_age(
Duration::from_secs(10 * 60)
.try_into()
.unwrap(),
)
.build()
}
pub async fn get_records_page(
Path(zone_name): Path<String>,
State(app): State<AppState>,
OriginalUri(url): OriginalUri,
Extension(lang): Extension<LanguageIdentifier>,
) -> Result<Template<'static, Value>, Error> {
cookie_jar: PrivateCookieJar,
) -> Result<Response, Error> {
let zone = app.db.get_zone_by_name(&zone_name).await?;
let records = zone.get_records(app.records).await?;
let records = friendly::FriendlyRecords::from(records);
Ok(Template::new(
"pages/records.html",
app.template_engine,
json!({
"current_zone": zone.name,
"records": records,
"url": url.to_string(),
"lang": lang.to_string(),
})
))
let flash_message = cookie_jar.get(COOKIE_FLASH).map(|cookie| cookie.value().to_owned());
let cookie = make_cookie(COOKIE_FLASH.into(), "".into());
let cookie_jar = cookie_jar.remove(cookie);
Ok(
(
cookie_jar,
Template::new(
"pages/records.html",
app.template_engine,
json!({
"current_zone": zone.name,
"records": records,
"url": url.to_string(),
"lang": lang.to_string(),
"flash_message": flash_message
})
)
).into_response()
)
}
@ -76,7 +109,8 @@ pub async fn post_new_record(
Query(mut params): Query<NewRecordQuery>,
OriginalUri(url): OriginalUri,
Extension(lang): Extension<LanguageIdentifier>,
form: Node,
cookie_jar: PrivateCookieJar,
form: Form,
) -> Result<Response, Error> {
let zone = app.db.get_zone_by_name(&zone_name).await?;
let mut errors = Vec::new();
@ -112,7 +146,14 @@ pub async fn post_new_record(
internal::RecordList::new(&new_records)
).await?;
Ok(().into_response())
let cookie = make_cookie(COOKIE_FLASH.into(), "configure-success".into());
let cookie_jar = cookie_jar.add(cookie);
Ok((
cookie_jar,
// TODO: change base URL in URL
Redirect::to(&format!("http://localhost:8000/zones/{}/records", zone.name)),
).into_response())
} else {
Ok(Template::new(
"pages/new_record.html",

View file

@ -1,4 +1,4 @@
{% macro rrset(record, zone, lang) %}
{% macro rrset(record, name, url, lang) %}
<li class="rrset">
<div class="rtype">
{%- if record.record_type == "service" -%}
@ -11,7 +11,7 @@
{{ tr(msg="zone-content-record-type-" ~ record.record_type, attr="type-name", lang=lang) }}
{%- endif -%}
<div class="action">
<a class="button icon" href="#">
<a class="button icon" href="{{ url }}/new?name={{ name }}&rtype={{ record.record_type }}">
{{ icons::pencil() }}
</a>
</div>

View file

@ -22,15 +22,21 @@
{{ records | json_encode(pretty=true) }}
</pre>
-->
{% if flash_message == "configure-success" %}
<p class="flash success">
{{ tr(msg="zone-content-flash-message", attr="configure-success", lang=lang) }}
</p>
{% endif %}
<section>
<h2>{{ tr(msg="zone-content-records-header", lang=lang) }}</h2>
{%- for node in records.records -%}
{% set name = node.name | trim_end_matches(pat=current_zone) | trim_end_matches(pat=".") %}
<article class="domain">
<header>
<h3 class="folder-tab">{{ node.name }}</h3>
<span class="sep"></span>
<a href="{{ url }}/new?name={{ node.name | trim_end_matches(pat=current_zone) | trim_end_matches(pat=".") }}" class="button">
<a href="{{ url }}/new?name={{ name }}" class="button">
{{ icons::plus_circle() }}
{{ tr(msg="zone-content-new-record-button", lang=lang) }}
</a>
@ -44,7 +50,8 @@
{%- if records.addresses -%}
{{ rrset::rrset(
record=records.addresses.0,
zone=current_zone,
name=name,
url=url,
lang=lang) }}
{%- endif -%}
</ul>
@ -57,25 +64,29 @@
{%- if records.mailservers -%}
{{ rrset::rrset(
record=records.mailservers.0,
zone=current_zone,
name=name,
url=url,
lang=lang) }}
{%- endif -%}
{%- if records.spf -%}
{{ rrset::rrset(
record=records.spf.0,
zone=current_zone,
name=name,
url=url,
lang=lang) }}
{%- endif -%}
{%- if records.dkim -%}
{{ rrset::rrset(
record=records.dkim.0,
zone=current_zone,
name=name,
url=url,
lang=lang) }}
{%- endif -%}
{%- if records.dmarc -%}
{{ rrset::rrset(
record=records.dmarc.0,
zone=current_zone,
name=name,
url=url,
lang=lang) }}
{%- endif -%}
</ul>
@ -87,7 +98,8 @@
{%- for service in sections.services -%}
{{ rrset::rrset(
record=service,
zone=current_zone,
name=name,
url=url,
lang=lang) }}
{%- endfor -%}
</ul>
@ -99,7 +111,8 @@
{%- for record in sections.miscellaneous -%}
{{ rrset::rrset(
record=record,
zone=current_zone,
name=name,
url=url,
lang=lang) }}
{%- endfor -%}
</ul>