diff --git a/services/reports/Cargo.lock b/services/reports/Cargo.lock --- a/services/reports/Cargo.lock +++ b/services/reports/Cargo.lock @@ -1532,6 +1532,28 @@ "regex-automata 0.1.10", ] +[[package]] +name = "maud" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0bab19cef8a7fe1c18a43e881793bfc9d4ea984befec3ae5bd0415abf3ecf00" +dependencies = [ + "itoa", + "maud_macros", +] + +[[package]] +name = "maud_macros" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be95d66c3024ffce639216058e5bae17a83ecaf266ffc6e4d060ad447c9eed2" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1799,6 +1821,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -1913,6 +1959,7 @@ "comm-services-lib", "derive_more", "http", + "maud", "num-derive", "num-traits", "once_cell", diff --git a/services/reports/Cargo.toml b/services/reports/Cargo.toml --- a/services/reports/Cargo.toml +++ b/services/reports/Cargo.toml @@ -19,6 +19,7 @@ ] } derive_more = "0.99" http = "0.2" +maud = "0.25" num-traits = "0.2" num-derive = "0.4" once_cell = "1.17" diff --git a/services/reports/src/email/mod.rs b/services/reports/src/email/mod.rs --- a/services/reports/src/email/mod.rs +++ b/services/reports/src/email/mod.rs @@ -1 +1,2 @@ pub mod config; +mod template; diff --git a/services/reports/src/email/template/html_layout.rs b/services/reports/src/email/template/html_layout.rs new file mode 100644 --- /dev/null +++ b/services/reports/src/email/template/html_layout.rs @@ -0,0 +1,89 @@ +use maud::{html, Markup, PreEscaped, DOCTYPE}; + +pub fn render_page(body: Markup) -> String { + let markup = html! { + (DOCTYPE) + html lang="en" { + head { + meta charset="utf-8"; + style { (PreEscaped(CSS)) } + } + body { + (body) + } + } + }; + + markup.into_string() +} + +const CSS: &str = r#" +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 20px; + color: #333; + line-height: 1.5; +} + +h2 { + font-size: 24px; + color: #222; + margin: 0 0 10px; + padding: 0; +} + +h3 { + font-size: 18px; + color: #444; + margin: 0 0 8px; + padding: 0; +} + +h4 { + font-size: 16px; + color: #555; + margin: 0; + padding: 0; +} + +p { + margin: 0 0 10px; + padding: 0; +} + +b { + font-weight: bold; +} + +em { + font-style: italic; +} + +code { + background-color: #f0f0f0; + padding: 2px 4px; + border-radius: 2px; + font-family: monospace; +} + +pre { + background-color: #f0f0f0; + padding: 10px; + font-family: monospace; + overflow: auto; +} + +ul { + margin: 0 0 10px 20px; + padding: 0; +} + +a { + color: #007bff; + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +"#; diff --git a/services/reports/src/email/template/mod.rs b/services/reports/src/email/template/mod.rs new file mode 100644 --- /dev/null +++ b/services/reports/src/email/template/mod.rs @@ -0,0 +1,51 @@ +use maud::{html, Markup, Render}; + +use crate::report_types::*; + +mod html_layout; + +pub fn render_email_for_report( + report_input: &ReportInput, + report_id: &ReportID, + user_id: Option<&str>, +) -> String { + html_layout::render_page(message_body_for_report( + report_input, + report_id, + user_id, + )) +} + +fn message_body_for_report( + report: &ReportInput, + report_id: &ReportID, + user_id: Option<&str>, +) -> Markup { + let user = user_id.unwrap_or("N/A"); + let platform = &report.platform_details; + let time = report + .time + .unwrap_or_default() + .format("%d/%m/%y %H:%M:%S") + .to_string(); + + html! { + ul { + li { "User ID: " b { (user) } } + li { "Platform: " (platform) } + li { "Time: " b { (time) } } + } + } +} + +impl Render for PlatformDetails { + fn render(&self) -> Markup { + let code_version = self + .code_version + .map(|it| it.to_string()) + .unwrap_or("N/A".into()); + html! { + b { (self.platform) } ", code version: " b { (code_version) } + } + } +} diff --git a/services/reports/src/report_types.rs b/services/reports/src/report_types.rs --- a/services/reports/src/report_types.rs +++ b/services/reports/src/report_types.rs @@ -41,6 +41,23 @@ UserInconsistency = 4, } +impl ReportType { + pub fn is_error(&self) -> bool { + matches!(self, Self::ErrorReport) + } + pub fn is_media_mission(&self) -> bool { + matches!(self, Self::MediaMission) + } + pub fn is_inconsistency(&self) -> bool { + matches!( + self, + ReportType::ThreadInconsistency + | ReportType::EntryInconsistency + | ReportType::UserInconsistency + ) + } +} + /// Report platform #[derive(Clone, Debug, Serialize, Deserialize, Display)] #[serde(rename_all = "lowercase")] @@ -56,8 +73,8 @@ #[serde(rename_all = "camelCase")] pub struct PlatformDetails { pub platform: ReportPlatform, - code_version: Option, - state_version: Option, + pub code_version: Option, + pub state_version: Option, } /// Input report payload - this is the JSON we receive from clients