diff --git a/peach-web/src/routes/settings/network/configure_dns.rs b/peach-web/src/routes/settings/network/configure_dns.rs index e69de29..a619234 100644 --- a/peach-web/src/routes/settings/network/configure_dns.rs +++ b/peach-web/src/routes/settings/network/configure_dns.rs @@ -0,0 +1,201 @@ +use log::info; +use maud::{html, Markup, PreEscaped}; +use peach_lib::{ + config_manager, dyndns_client, + error::PeachError, + jsonrpc_client_core::{Error, ErrorKind}, + jsonrpc_core::types::error::ErrorCode, +}; +use rouille::{post_input, try_or_400, Request, Response}; + +use crate::{ + error::PeachWebError, + templates, + utils::{ + flash::{FlashRequest, FlashResponse}, + theme, + }, +}; + +// ROUTE: /settings/network/dns + +fn render_dyndns_status_indicator() -> Markup { + let (indicator_class, indicator_label) = match dyndns_client::is_dns_updater_online() { + Ok(true) => ("success-border", "Dynamic DNS is currently online."), + _ => ( + "warning-border", + "Dynamic DNS is enabled but may be offline.", + ), + }; + + html! { + (PreEscaped("")) + div id="dyndns-status-indicator" class={ "stack capsule " (indicator_class) } { + div class="stack" { + label class="label-small font-near-black" { (indicator_label) } + } + } + } +} + +fn render_external_domain_input() -> Markup { + let external_domain = config_manager::get_config_value("EXTERNAL_DOMAIN").ok(); + + html! { + div class="input-wrapper" { + (PreEscaped("")) + label id="external_domain" class="label-small input-label font-near-black" { + label class="label-small input-label font-gray" for="external_domain" style="padding-top: 0.25rem;" { "External Domain (optional)" } + input id="external_domain" class="form-input" style="margin-bottom: 0;" name="external_domain" type="text" title="external domain" value=[external_domain]; + } + } + } +} + +fn render_dyndns_enabled_checkbox() -> Markup { + let dyndns_enabled = config_manager::get_dyndns_enabled_value().unwrap_or(false); + + html! { + div class="input-wrapper" { + div { + (PreEscaped("")) + label class="label-small input-label font-gray" { "Enable Dynamic DNS" } + input style="margin-left: 0px;" id="enable_dyndns" name="enable_dyndns" title="Activate dynamic DNS" type="checkbox" checked[dyndns_enabled]; + } + } + } +} + +fn render_dynamic_domain_input() -> Markup { + let dyndns_domain = + config_manager::get_config_value("DYN_DOMAIN").unwrap_or_else(|_| String::from("")); + let dyndns_subdomain = + dyndns_client::get_dyndns_subdomain(&dyndns_domain).unwrap_or(dyndns_domain); + + html! { + div class="input-wrapper" { + (PreEscaped("")) + label id="cut" class="label-small input-label font-near-black" { + label class="label-small input-label font-gray" for="cut" style="padding-top: 0.25rem;" { "Dynamic DNS Domain" } + input id="dyndns_domain" class="alert-input" name="dynamic_domain" placeholder="" type="text" title="dyndns_domain" value=(dyndns_subdomain); + { ".dyn.peachcloud.org" } + } + } + } +} + +fn render_save_button() -> Markup { + html! { + div id="buttonDiv" style="margin-top: 2rem;" { + input id="configureDNSButton" class="button button-primary center" title="Add" type="submit" value="Save"; + } + } +} + +/// DNS configuration form template builder. +pub fn build_template(request: &Request) -> PreEscaped { + let (flash_name, flash_msg) = request.retrieve_flash(); + + let dyndns_enabled = config_manager::get_dyndns_enabled_value().unwrap_or(false); + + let form_template = html! { + (PreEscaped("")) + div class="card center" { + @if dyndns_enabled { + (render_dyndns_status_indicator()) + } + form id="configureDNS" class="center" action="/settings/network/dns" method="post" { + (render_external_domain_input()) + (render_dyndns_enabled_checkbox()) + (render_dynamic_domain_input()) + (render_save_button()) + @if let (Some(name), Some(msg)) = (flash_name, flash_msg) { + (PreEscaped("")) + (templates::flash::build_template(name, msg)) + } + } + } + }; + + let body = templates::nav::build_template( + form_template, + "Configure Dynamic DNS", + Some("/settings/network"), + ); + + let theme = theme::get_theme(); + + templates::base::build_template(body, theme) +} + +pub fn save_dns_configuration( + external_domain: String, + enable_dyndns: bool, + dynamic_domain: String, +) -> Result<(), PeachWebError> { + // first save local configurations + config_manager::set_external_domain(&external_domain)?; + config_manager::set_dyndns_enabled_value(enable_dyndns)?; + + let full_dynamic_domain = dyndns_client::get_full_dynamic_domain(&dynamic_domain); + + // if dynamic dns is enabled and this is a new domain name, then register it + if enable_dyndns && dyndns_client::check_is_new_dyndns_domain(&full_dynamic_domain)? { + if let Err(registration_err) = dyndns_client::register_domain(&full_dynamic_domain) { + info!("Failed to register dyndns domain: {:?}", registration_err); + + // error message describing the failed update + let err_msg = match registration_err { + PeachError::JsonRpcClientCore(Error(ErrorKind::JsonRpcError(rpc_err), _)) => { + if let ErrorCode::ServerError(-32030) = rpc_err.code { + format!( + "Error registering domain: {} was previously registered", + full_dynamic_domain + ) + } else { + format!("Failed to register dyndns domain: {:?}", rpc_err) + } + } + _ => "Failed to register dyndns domain".to_string(), + }; + + Err(PeachWebError::FailedToRegisterDynDomain(err_msg)) + } else { + info!("Registered new dyndns domain"); + + Ok(()) + } + } else { + info!("Domain {} already registered", dynamic_domain); + + Ok(()) + } +} + +/// Parse the DNS configuration parameters and apply them. +pub fn handle_form(request: &Request) -> Response { + let data = try_or_400!(post_input!(request, { + external_domain: String, + enable_dyndns: bool, + dynamic_domain: String, + })); + + let (name, msg) = match save_dns_configuration( + data.external_domain, + data.enable_dyndns, + data.dynamic_domain, + ) { + Ok(_) => ( + "success".to_string(), + "New dynamic DNS configuration is now enabled".to_string(), + ), + Err(err) => ( + "error".to_string(), + format!("Failed to save DNS configuration: {}", err), + ), + }; + + let (flash_name, flash_msg) = (format!("flash_name={}", name), format!("flash_msg={}", msg)); + + Response::redirect_303("/settings/network/dns").add_flash(flash_name, flash_msg) +} diff --git a/peach-web/src/routes/settings/network/mod.rs b/peach-web/src/routes/settings/network/mod.rs index 36b4d6a..e9bf79e 100644 --- a/peach-web/src/routes/settings/network/mod.rs +++ b/peach-web/src/routes/settings/network/mod.rs @@ -1,2 +1,3 @@ +pub mod configure_dns; pub mod menu; pub mod modify_ap;