Skip to content

Commit 5867f1f

Browse files
authored
feat: support for email themes for rsvp (#59)
* feat: Implement theming support for email templates and refactor configuration * feat(email): Enhance ticket email template with theming support and dynamic colors * feat(email): Add RGB color validation and update email templates for theming support * revert config.py configurations
1 parent 2191dea commit 5867f1f

File tree

4 files changed

+303
-240
lines changed

4 files changed

+303
-240
lines changed

app/schemas/email.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from fastapi import HTTPException, status
88
from pydantic import BaseModel, EmailStr, field_validator, ValidationInfo
99
from app.config import config
10+
from app.schemas.theme import EmailTheme
1011

1112

1213
class EmailProvider(str, Enum):
@@ -30,11 +31,11 @@ class SendEmailRequestBody(BaseModel):
3031
id: int
3132
subject: str
3233
recipient: Optional[EmailStr] = None
33-
body: Dict[str, str]
34+
body: Dict[str, str | dict]
3435
cc: Optional[List[EmailStr]] = None
3536
bcc: Optional[List[EmailStr]] = None
3637
self: bool = False
37-
provider: EmailProvider = EmailProvider.SES
38+
provider: EmailProvider = EmailProvider.GMAIL
3839

3940
@field_validator("body")
4041
@classmethod
@@ -70,6 +71,19 @@ def check_body_for_id(cls, body: dict, values: ValidationInfo) -> dict:
7071

7172
return body
7273

74+
@field_validator("body")
75+
@classmethod
76+
def extract_theme(cls, body: dict) -> dict:
77+
"""
78+
Extracts theme from body if present, else assigns an empty dict.
79+
"""
80+
theme = body.get("theme")
81+
if theme:
82+
body["theme"] = EmailTheme(**theme).model_dump()
83+
else:
84+
body["theme"] = EmailTheme().model_dump()
85+
return body
86+
7387
@field_validator("cc", "bcc", mode="before")
7488
@classmethod
7589
def add_self_to_recipients(cls, value, values: ValidationInfo):

app/schemas/theme.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from pydantic import BaseModel, field_validator
2+
import re
3+
4+
HEX_COLOR = re.compile(r"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
5+
6+
RGB_COLOR = re.compile(
7+
r"^rgba?\(\s*"
8+
r"(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\s*,\s*"
9+
r"(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\s*,\s*"
10+
r"(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)"
11+
r"(?:\s*,\s*(0|1|0?\.\d+))?"
12+
r"\s*\)$"
13+
)
14+
15+
class EmailTheme(BaseModel):
16+
fontFamily: str = "Inter, Arial, sans-serif"
17+
primaryColor: str = "#ac6aff"
18+
backgroundColor: str = "#141415"
19+
textColor: str = "#ffffff"
20+
buttonTextColor: str = "#ffffff"
21+
secondaryColor: str = "#ac6aff"
22+
23+
@field_validator(
24+
"primaryColor",
25+
"backgroundColor",
26+
"textColor",
27+
"buttonTextColor",
28+
"fontFamily",
29+
"secondaryColor"
30+
)
31+
@classmethod
32+
def validate_color(cls, value: str) -> str:
33+
if not (HEX_COLOR.match(value) or RGB_COLOR.match(value)):
34+
raise ValueError(
35+
"Invalid color format. Use HEX (#fff, #ffffff) or RGB/RGBA."
36+
)
37+
return value

templates/rsvp/Verify.html

Lines changed: 87 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,97 @@
1-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1+
<!DOCTYPE html
2+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
23
<html dir="ltr" lang="en">
4+
35
<head>
4-
<link rel="preload" as="image" href="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/logo.png" />
5-
<link rel="preload" as="image" href="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/verification-img.png" />
6-
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
7-
<meta name="x-apple-disable-message-reformatting" />
6+
<link rel="preload" as="image"
7+
href="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/logo.png" />
8+
<link rel="preload" as="image"
9+
href="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/verification-img.png" />
10+
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
11+
<meta name="x-apple-disable-message-reformatting" />
812
</head>
13+
914
<body style="background: #141415; font-family: 'Inter', Verdana; padding-top: 2.5rem; padding-bottom: 2.5rem;">
10-
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="max-width: 37.5em; margin: 0 auto; padding-left: 1.5rem; padding-right: 1.5rem;">
15+
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
16+
style="max-width: 37.5em; margin: 0 auto; padding-left: 1.5rem; padding-right: 1.5rem;">
1117
<tbody>
12-
<tr>
18+
<tr>
1319
<td>
14-
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
15-
<tbody>
16-
<tr>
17-
<td>
18-
<img alt="Team shiksha logo" src="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/logo.png" width="100" height="50" style="display: block; outline: none; border: none; text-decoration: none;" />
19-
</td>
20-
</tr>
21-
</tbody>
22-
</table>
23-
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
24-
<tbody>
25-
<tr>
26-
<td>
27-
<img src="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/verification-img.png" style="display: block; outline: none; border: none; text-decoration: none; height: auto; width: 100%;" />
28-
<p style="font-size: 2.25rem; line-height: 2.5rem; margin: 16px 0; margin-top: 2.75rem; font-weight: 600; color: rgb(255, 255, 255);">Hi</p>
29-
<p style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">
30-
We're excited to have you join our community of creators! To securely access your account, simply click the magic link below:
31-
</p>
32-
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background-color: #ac6aff; font-size: 16px; width: fit-content; padding: 10px 20px; border-radius: 20px; margin-left: 0;">
33-
<tbody>
34-
<tr>
35-
<td>
36-
<a href="{{magicLink}}" target="_blank" style="color: white; text-decoration: none; font-size: 14px;">Click here to login to your account</a>
37-
</td>
38-
</tr>
39-
</tbody>
40-
</table>
41-
<p style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">
42-
This link will expire in 15 Minutes. Once inside, you'll have access to our full suite of intuitive events and can create one. Our team is here to support you every step of the way.
43-
</p>
44-
<p style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">Happy Unforgettable events!</p>
45-
<p style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">Team Shiksha</p>
46-
</td>
47-
</tr>
48-
</tbody>
49-
</table>
50-
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
51-
<tbody>
52-
<tr>
53-
<td>
54-
<p style="font-size: 0.875rem; line-height: 1.25rem; margin: 16px 0; color: rgb(255, 255, 255);">
55-
This email was sent to <span>{{email}}</span>.
56-
</p>
57-
<p style="font-size: 0.875rem; line-height: 1.25rem; margin: 16px 0; color: rgb(255, 255, 255);">
58-
© 2025 <a href="https://rsvp.kim" target="_blank" style="color: #ac6aff; text-decoration: none;">RSVP.KIM</a>. All rights reserved. Powered by <a href="https://team.shiksha/" target="_blank" style="color: #ac6aff; text-decoration: none;">Team Shiksha</a>
59-
</p>
60-
</td>
61-
</tr>
62-
</tbody>
63-
</table>
20+
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
21+
<tbody>
22+
<tr>
23+
<td>
24+
<img alt="Team shiksha logo"
25+
src="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/logo.png"
26+
width="100" height="50"
27+
style="display: block; outline: none; border: none; text-decoration: none;" />
28+
</td>
29+
</tr>
30+
</tbody>
31+
</table>
32+
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
33+
<tbody>
34+
<tr>
35+
<td>
36+
<img
37+
src="https://raw.githubusercontent.com/TeamShiksha/email-service/refs/heads/main/images/verification-img.png"
38+
style="display: block; outline: none; border: none; text-decoration: none; height: auto; width: 100%;" />
39+
<p
40+
style="font-size: 2.25rem; line-height: 2.5rem; margin: 16px 0; margin-top: 2.75rem; font-weight: 600; color: rgb(255, 255, 255);">
41+
Hi</p>
42+
<p
43+
style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">
44+
We're excited to have you join our community of creators! To securely access your account, simply
45+
click the magic link below:
46+
</p>
47+
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
48+
style="background-color: #ac6aff; font-size: 16px; width: fit-content; padding: 10px 20px; border-radius: 20px; margin-left: 0;">
49+
<tbody>
50+
<tr>
51+
<td>
52+
<a href="{{magicLink}}" target="_blank"
53+
style="color: white; text-decoration: none; font-size: 14px;">Click here to login to your
54+
account</a>
55+
</td>
56+
</tr>
57+
</tbody>
58+
</table>
59+
<p
60+
style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">
61+
This link will expire in 15 Minutes. Once inside, you'll have access to our full suite of intuitive
62+
events and can create one. Our team is here to support you every step of the way.
63+
</p>
64+
<p
65+
style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">
66+
Happy Unforgettable events!</p>
67+
<p
68+
style="font-size: 1.125rem; line-height: 1.75rem; margin: 16px 0; margin-top: 1.5rem; color: rgb(255, 255, 255);">
69+
Team Shiksha</p>
70+
</td>
71+
</tr>
72+
</tbody>
73+
</table>
74+
<table align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
75+
<tbody>
76+
<tr>
77+
<td>
78+
<p style="font-size: 0.875rem; line-height: 1.25rem; margin: 16px 0; color: rgb(255, 255, 255);">
79+
This email was sent to <span>{{email}}</span>.
80+
</p>
81+
<p style="font-size: 0.875rem; line-height: 1.25rem; margin: 16px 0; color: rgb(255, 255, 255);">
82+
© 2025 <a href="https://rsvp.kim" target="_blank"
83+
style="color: #ac6aff; text-decoration: none;">RSVP.KIM</a>. All rights reserved. Powered by <a
84+
href="https://team.shiksha/" target="_blank" style="color: #ac6aff; text-decoration: none;">Team
85+
Shiksha</a>
86+
</p>
87+
</td>
88+
</tr>
89+
</tbody>
90+
</table>
6491
</td>
65-
</tr>
92+
</tr>
6693
</tbody>
67-
</table>
94+
</table>
6895
</body>
96+
6997
</html>

0 commit comments

Comments
 (0)