Skip to content

Commit a1613ca

Browse files
authored
Notification Preferences and templates (#4)
1 parent 219e11a commit a1613ca

File tree

10 files changed

+837
-49
lines changed

10 files changed

+837
-49
lines changed

Cargo.lock

Lines changed: 84 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ nostr-sqldb = { git = "https://github.com/BitcreditProtocol/nostr.git", branch =
1515
] }
1616
tokio = "1.45.1"
1717
axum = { version = "0.8.3", features = ["ws", "tokio"] }
18+
axum-extra = { version = "0.10", features = ["form"] }
1819
axum-raw-websocket = { git = "https://github.com/tompro/axum-raw-websocket.git" }
1920
anyhow = "1"
2021
tracing = "0.1.41"
2122
tracing-subscriber = "0.3.19"
22-
tower-http = { version = "0.6.6", features = ["cors"] }
23+
tower-http = { version = "0.6.6", features = ["cors", "fs"] }
2324
clap = { version = "4.5.39", features = ["derive", "env"] }
2425
chrono = "0.4"
2526
serde_json = "1"
@@ -34,3 +35,4 @@ email_address = "0.2"
3435
bitflags = "2.9"
3536
reqwest = { version = "0.12", default-features = false, features = ["default", "json"] }
3637
base64 = "0.22"
38+
tinytemplate = "1.2"

src/main.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ use clap::Parser;
2020
use nostr::types::Url;
2121
use nostr_relay_builder::LocalRelay;
2222
use relay::RelayConfig;
23-
use tower_http::cors::{Any, CorsLayer};
23+
use tower_http::{
24+
cors::{Any, CorsLayer},
25+
services::ServeDir,
26+
};
2427
use tracing::{error, info};
2528

2629
use crate::notification::{
@@ -45,6 +48,7 @@ async fn main() -> Result<()> {
4548

4649
let app_state = AppState::new(&config).await?;
4750
let app = Router::new()
51+
.nest_service("/static", ServeDir::new("./static"))
4852
.route("/list/{pub_key}", get(blossom::handle_list))
4953
.route("/mirror", put(blossom::handle_mirror))
5054
.route("/media", any(blossom::handle_media))
@@ -60,6 +64,14 @@ async fn main() -> Result<()> {
6064
"/notifications/confirm_email",
6165
get(notification::confirm_email),
6266
)
67+
.route(
68+
"/notifications/preferences/{token}",
69+
get(notification::preferences),
70+
)
71+
.route(
72+
"/notifications/update_preferences",
73+
post(notification::update_preferences),
74+
)
6375
.route("/", any(websocket_handler))
6476
.fallback(handle_404)
6577
.with_state(app_state)

src/notification/email/mod.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use async_trait::async_trait;
2+
use serde::Serialize;
3+
use tinytemplate::TinyTemplate;
4+
5+
use crate::util::get_logo_link;
26

37
pub mod mailjet;
8+
mod template;
49

510
#[async_trait]
611
pub trait EmailService: Send + Sync {
@@ -16,27 +21,38 @@ pub struct EmailMessage {
1621
pub body: String,
1722
}
1823

24+
#[derive(Serialize)]
25+
struct EmailConfirmationContext {
26+
pub logo_link: String,
27+
pub link: String,
28+
}
29+
1930
pub fn build_email_confirmation_message(
2031
host_url: &url::Url,
2132
from: &str,
2233
to: &str,
2334
token: &str,
24-
) -> EmailMessage {
35+
) -> Result<EmailMessage, anyhow::Error> {
36+
let mut tt = TinyTemplate::new();
37+
tt.add_template("mail", template::MAIL_CONFIRMATION_TEMPLATE)?;
38+
2539
// build email confirmation link
2640
let mut link = host_url
2741
.join("/notifications/confirm_email")
2842
.expect("email confirmation mail");
2943
link.set_query(Some(&format!("token={token}")));
3044

31-
// build template
32-
let body = format!(
33-
"<html><head></head><body><a href=\"{link}\">Click here to confirm</a><br /><br />This link is valid for 1 day.</body></html>"
34-
);
45+
let context = EmailConfirmationContext {
46+
logo_link: get_logo_link(host_url),
47+
link: link.to_string(),
48+
};
49+
50+
let rendered = tt.render("mail", &context)?;
3551

36-
EmailMessage {
52+
Ok(EmailMessage {
3753
from: from.to_owned(),
3854
to: to.to_owned(),
39-
subject: "Confirm your E-Mail".to_owned(),
40-
body: body.to_owned(),
41-
}
55+
subject: "Please confirm your E-Mail".to_owned(),
56+
body: rendered,
57+
})
4258
}

src/notification/email/template.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
pub const MAIL_CONFIRMATION_TEMPLATE: &str = r#"
2+
<!doctype html>
3+
<html lang="en">
4+
<head>
5+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Confirm your E-Mail</title>
8+
</head>
9+
<body style="margin:0; padding:0; background:#ffffff;">
10+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%">
11+
<tr>
12+
<td align="center">
13+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="650" class="container" style="width:650px; max-width:650px;">
14+
<tr>
15+
<td class="px" style="padding:18px 24px; background:#fefbf1;">
16+
<img src="{logo_link}"
17+
alt="Bitcredit" width="120" height="24"
18+
style="display:block; border:0; outline:none; text-decoration:none; height:auto;">
19+
</td>
20+
</tr>
21+
</table>
22+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="650" class="container" style="width:650px; max-width:650px; background:#ffffff;">
23+
<tr style="background: #fefbf1;">
24+
<td class="px" style="padding:15px 24px 8px 24px; font-family:Geist, system-ui, sans-serif; color:#111111;">
25+
<h1 style="margin:0; font-size:24px; line-height:36px; font-weight:500;">
26+
Please confirm your E-Mail
27+
</h1>
28+
</td>
29+
</tr>
30+
<tr>
31+
<td align="center" style="padding:60px 24px 30px 24px;">
32+
<a href="{link}"
33+
style="background:#2b2118; color:#ffffff; text-decoration:none; display:inline-block;
34+
font-family:Geist, system-ui, sans-serif; font-size:14px; font-weight: 500;
35+
padding:12px 24px; border-radius:.5rem;">
36+
Click here to confirm
37+
</a>
38+
</td>
39+
</tr>
40+
<tr>
41+
<td align="center" class="px" style="padding:0px 24px 28px 24px; font-family:Geist, system-ui, sans-serif; font-size:13px; line-height:20px; color:#333333;">
42+
The link is valid for 1 day.
43+
</td>
44+
</tr>
45+
</table>
46+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="650" class="container" style="width:650px; max-width:650px;">
47+
<tr><td style="height:24px; line-height:24px;">&nbsp;</td></tr>
48+
</table>
49+
</td>
50+
</tr>
51+
</table>
52+
</body>
53+
</html>
54+
"#;
55+
56+
#[allow(unused)]
57+
pub const NOTIFICATION_MAIL_TEMPLATE: &str = r#"
58+
<!doctype html>
59+
<html lang="en">
60+
<head>
61+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
62+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
63+
<title>{title}</title>
64+
</head>
65+
<body style="margin:0; padding:0; background:#ffffff;">
66+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%">
67+
<tr>
68+
<td align="center">
69+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="650" class="container" style="width:650px; max-width:650px;">
70+
<tr>
71+
<td class="px" style="padding:18px 24px; background:#fefbf1;">
72+
<img src="{logo_link}"
73+
alt="Bitcredit" width="120" height="24"
74+
style="display:block; border:0; outline:none; text-decoration:none; height:auto;">
75+
</td>
76+
</tr>
77+
</table>
78+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="650" class="container" style="width:650px; max-width:650px; background:#ffffff;">
79+
<tr style="background: #fefbf1;">
80+
<td class="px" style="padding:15px 24px 8px 24px; font-family:Geist, system-ui, sans-serif; color:#111111;">
81+
<h1 style="margin:0; font-size:24px; line-height:36px; font-weight:500;">
82+
{title}
83+
</h1>
84+
</td>
85+
</tr>
86+
<tr>
87+
<td align="center" style="padding:60px 24px 36px 24px;">
88+
<a href="{link}"
89+
style="background:#2b2118; color:#ffffff; text-decoration:none; display:inline-block;
90+
font-family:Geist, system-ui, sans-serif; font-size:14px; font-weight: 500;
91+
padding:12px 24px; border-radius:.5rem;">
92+
Go to eBill
93+
</a>
94+
</td>
95+
</tr>
96+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="650" class="container" style="width:650px; max-width:650px;">
97+
<tr><td style="height:44px; line-height:44px;">&nbsp;</td></tr>
98+
</table>
99+
<tr>
100+
<td align="center" class="px" style="padding:16px 24px 28px 24px; font-family:Geist, system-ui, sans-serif; font-size:13px; line-height:20px; color:#333333;">
101+
<a href="{notification_link}" style="color:#333333; text-decoration:none;">Manage notification settings</a>
102+
&nbsp;&nbsp;&nbsp;&nbsp;
103+
<a href="{browser_link}" style="color:#333333; text-decoration:none;">View in the browser</a>
104+
</td>
105+
</tr>
106+
</table>
107+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="650" class="container" style="width:650px; max-width:650px;">
108+
<tr><td style="height:24px; line-height:24px;">&nbsp;</td></tr>
109+
</table>
110+
</td>
111+
</tr>
112+
</table>
113+
</body>
114+
</html>
115+
"#;

0 commit comments

Comments
 (0)