1use crate::prelude::*;
2use utoipa::ToSchema;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, sqlx::Type, ToSchema)]
5#[sqlx(type_name = "email_template_type", rename_all = "snake_case")]
6#[serde(rename_all = "snake_case")]
7pub enum EmailTemplateType {
8 ResetPasswordEmail,
9 DeleteUserEmail,
10 ConfirmEmailCode,
11 Generic,
12}
13
14#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
15
16pub struct EmailTemplate {
17 pub id: Uuid,
18 pub created_at: DateTime<Utc>,
19 pub updated_at: DateTime<Utc>,
20 pub deleted_at: Option<DateTime<Utc>>,
21 pub content: Option<serde_json::Value>,
22 #[serde(rename = "template_type")]
23 #[schema(rename = "template_type")]
24 pub email_template_type: EmailTemplateType,
25 pub subject: Option<String>,
26 pub exercise_completions_threshold: Option<i32>,
27 pub points_threshold: Option<i32>,
28 pub course_id: Option<Uuid>,
29 pub language: Option<String>,
30}
31
32#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
33
34pub struct EmailTemplateNew {
35 pub template_type: EmailTemplateType,
36 pub language: Option<String>,
37 pub content: Option<serde_json::Value>,
38 pub subject: Option<String>,
39}
40
41#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
42
43pub struct EmailTemplateUpdate {
44 pub template_type: EmailTemplateType,
45 pub subject: String,
46 pub content: serde_json::Value,
47 pub exercise_completions_threshold: Option<i32>,
48 pub points_threshold: Option<i32>,
49}
50
51pub async fn get_email_templates(
52 conn: &mut PgConnection,
53 course_id: Uuid,
54) -> ModelResult<Vec<EmailTemplate>> {
55 let res = sqlx::query_as!(
56 EmailTemplate,
57 r#"
58SELECT *
59FROM email_templates
60WHERE course_id = $1
61 AND deleted_at IS NULL
62 "#,
63 course_id
64 )
65 .fetch_all(conn)
66 .await?;
67 Ok(res)
68}
69
70pub async fn get_all_email_templates(conn: &mut PgConnection) -> ModelResult<Vec<EmailTemplate>> {
71 let res = sqlx::query_as!(
72 EmailTemplate,
73 r#"
74SELECT *
75FROM email_templates
76WHERE deleted_at IS NULL
77 "#,
78 )
79 .fetch_all(conn)
80 .await?;
81 Ok(res)
82}
83
84pub async fn get_generic_email_template_by_type_and_language(
85 conn: &mut PgConnection,
86 template_type: EmailTemplateType,
87 language: &str,
88) -> ModelResult<EmailTemplate> {
89 let res = sqlx::query_as!(
90 EmailTemplate,
91 r#"
92SELECT *
93FROM email_templates
94WHERE email_template_type = $1
95 AND course_id IS NULL
96 AND deleted_at IS NULL
97 AND (
98 language = $2
99 OR language = 'en'
100 OR language IS NULL
101 )
102ORDER BY CASE
103 WHEN language = $2 THEN 0
104 WHEN language = 'en' THEN 1
105 WHEN language IS NULL THEN 2
106 ELSE 3
107 END
108LIMIT 1
109 "#,
110 template_type as EmailTemplateType,
111 language
112 )
113 .fetch_one(conn)
114 .await?;
115 Ok(res)
116}
117
118pub async fn insert_email_template(
119 conn: &mut PgConnection,
120 course_id: Option<Uuid>,
121 email_template: EmailTemplateNew,
122 subject: Option<&'_ str>,
123) -> ModelResult<EmailTemplate> {
124 let subject_to_use = email_template.subject.as_deref().or(subject);
125 let res = sqlx::query_as!(
126 EmailTemplate,
127 r#"
128INSERT INTO email_templates (
129 email_template_type,
130 course_id,
131 subject,
132 language,
133 content
134 )
135VALUES ($1, $2, $3, $4, $5) ON CONFLICT (email_template_type, language, deleted_at)
136WHERE course_id IS NULL
137 AND deleted_at IS NULL DO
138UPDATE
139SET subject = COALESCE(EXCLUDED.subject, email_templates.subject),
140 content = COALESCE(EXCLUDED.content, email_templates.content),
141 updated_at = NOW()
142RETURNING *
143 "#,
144 email_template.template_type as EmailTemplateType,
145 course_id,
146 subject_to_use,
147 email_template.language,
148 email_template.content,
149 )
150 .fetch_one(conn)
151 .await?;
152 Ok(res)
153}
154
155pub async fn get_email_template(
156 conn: &mut PgConnection,
157 email_template_id: Uuid,
158) -> ModelResult<EmailTemplate> {
159 let res = sqlx::query_as!(
160 EmailTemplate,
161 r#"
162SELECT *
163FROM email_templates
164WHERE id = $1
165 AND deleted_at IS NULL
166 "#,
167 email_template_id
168 )
169 .fetch_one(conn)
170 .await?;
171 Ok(res)
172}
173
174pub async fn update_email_template(
175 conn: &mut PgConnection,
176 email_template_id: Uuid,
177 email_template_update: EmailTemplateUpdate,
178) -> ModelResult<EmailTemplate> {
179 let res = sqlx::query_as!(
180 EmailTemplate,
181 r#"
182UPDATE email_templates
183SET email_template_type = $1,
184 subject = $2,
185 content = $3,
186 exercise_completions_threshold = $4,
187 points_threshold = $5
188WHERE id = $6
189 AND deleted_at IS NULL
190RETURNING *
191 "#,
192 email_template_update.template_type as EmailTemplateType,
193 email_template_update.subject,
194 email_template_update.content,
195 email_template_update.exercise_completions_threshold,
196 email_template_update.points_threshold,
197 email_template_id
198 )
199 .fetch_one(conn)
200 .await?;
201 Ok(res)
202}
203
204pub async fn delete_email_template(
205 conn: &mut PgConnection,
206 email_template_id: Uuid,
207) -> ModelResult<EmailTemplate> {
208 let deleted = sqlx::query_as!(
209 EmailTemplate,
210 r#"
211UPDATE email_templates
212SET deleted_at = NOW()
213WHERE id = $1
214 AND deleted_at IS NULL
215RETURNING *
216 "#,
217 email_template_id
218 )
219 .fetch_one(conn)
220 .await?;
221 Ok(deleted)
222}