You've already forked AstralRinth
forked from didirus/AstralRinth
Custom Emails (#4526)
* Dynamic email template * Set lower cache expiry for templates * Custom email route * Fix subject line on custom emails * chore: query cache, clippy, fmt * Bugfixes * Key-based caching on custom emails * Sequentially process emails prone to causing cache stampede * Fill variables in dynamic body + subject line * Update apps/labrinth/src/queue/email/templates.rs Co-authored-by: aecsocket <aecsocket@tutanota.com> Signed-off-by: François-Xavier Talbot <108630700+fetchfern@users.noreply.github.com> * Update apps/labrinth/src/queue/email/templates.rs Co-authored-by: aecsocket <aecsocket@tutanota.com> Signed-off-by: François-Xavier Talbot <108630700+fetchfern@users.noreply.github.com> --------- Signed-off-by: François-Xavier Talbot <108630700+fetchfern@users.noreply.github.com> Co-authored-by: aecsocket <aecsocket@tutanota.com>
This commit is contained in:
committed by
GitHub
parent
aec49cff7c
commit
0c66fa3f12
@@ -1,14 +1,18 @@
|
||||
use crate::database::models::DatabaseError;
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::models::v3::notifications::{NotificationChannel, NotificationType};
|
||||
use crate::routes::ApiError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const TEMPLATES_NAMESPACE: &str = "notifications_templates";
|
||||
const TEMPLATES_HTML_DATA_NAMESPACE: &str = "notifications_templates_html_data";
|
||||
const TEMPLATES_DYNAMIC_HTML_NAMESPACE: &str =
|
||||
"notifications_templates_dynamic_html";
|
||||
|
||||
const HTML_DATA_CACHE_EXPIRY: i64 = 60 * 15; // 15 minutes
|
||||
const TEMPLATES_CACHE_EXPIRY: i64 = 60 * 30; // 30 minutes
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
pub struct NotificationTemplate {
|
||||
pub id: i64,
|
||||
pub channel: NotificationChannel,
|
||||
@@ -75,7 +79,7 @@ impl NotificationTemplate {
|
||||
TEMPLATES_NAMESPACE,
|
||||
channel.as_str(),
|
||||
&templates,
|
||||
None,
|
||||
Some(TEMPLATES_CACHE_EXPIRY),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -111,3 +115,44 @@ impl NotificationTemplate {
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_or_set_cached_dynamic_html<F>(
|
||||
redis: &RedisPool,
|
||||
key: &str,
|
||||
get: impl FnOnce() -> F,
|
||||
) -> Result<String, ApiError>
|
||||
where
|
||||
F: Future<Output = Result<String, ApiError>>,
|
||||
{
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct HtmlBody {
|
||||
html: String,
|
||||
}
|
||||
|
||||
let mut redis_conn = redis.connect().await?;
|
||||
if let Some(body) = redis_conn
|
||||
.get_deserialized_from_json::<HtmlBody>(
|
||||
TEMPLATES_DYNAMIC_HTML_NAMESPACE,
|
||||
key,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
return Ok(body.html);
|
||||
}
|
||||
|
||||
drop(redis_conn);
|
||||
|
||||
let cached = HtmlBody { html: get().await? };
|
||||
let mut redis_conn = redis.connect().await?;
|
||||
|
||||
redis_conn
|
||||
.set_serialized_to_json(
|
||||
TEMPLATES_DYNAMIC_HTML_NAMESPACE,
|
||||
key,
|
||||
&cached,
|
||||
Some(HTML_DATA_CACHE_EXPIRY),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(cached.html)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user