API - refactor imports
This commit is contained in:
0
fittrackee/emails/__init__.py
Normal file
0
fittrackee/emails/__init__.py
Normal file
117
fittrackee/emails/email.py
Normal file
117
fittrackee/emails/email.py
Normal file
@ -0,0 +1,117 @@
|
||||
import logging
|
||||
import smtplib
|
||||
import ssl
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from typing import Dict, Optional, Type, Union
|
||||
|
||||
from flask import Flask
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from .utils_email import parse_email_url
|
||||
|
||||
email_log = logging.getLogger('fittrackee_api_email')
|
||||
email_log.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
class EmailMessage:
|
||||
def __init__(
|
||||
self, sender: str, recipient: str, subject: str, html: str, text: str
|
||||
) -> None:
|
||||
self.sender = sender
|
||||
self.recipient = recipient
|
||||
self.subject = subject
|
||||
self.html = html
|
||||
self.text = text
|
||||
|
||||
def generate_message(self) -> MIMEMultipart:
|
||||
message = MIMEMultipart('alternative')
|
||||
message['Subject'] = self.subject
|
||||
message['From'] = self.sender
|
||||
message['To'] = self.recipient
|
||||
part1 = MIMEText(self.text, 'plain')
|
||||
part2 = MIMEText(self.html, 'html')
|
||||
message.attach(part1)
|
||||
message.attach(part2)
|
||||
return message
|
||||
|
||||
|
||||
class EmailTemplate:
|
||||
def __init__(self, template_directory: str) -> None:
|
||||
self._env = Environment(loader=FileSystemLoader(template_directory))
|
||||
|
||||
def get_content(
|
||||
self, template_name: str, lang: str, part: str, data: Dict
|
||||
) -> str:
|
||||
template = self._env.get_template(f'{template_name}/{lang}/{part}')
|
||||
return template.render(data)
|
||||
|
||||
def get_all_contents(self, template: str, lang: str, data: Dict) -> Dict:
|
||||
output = {}
|
||||
for part in ['subject.txt', 'body.txt', 'body.html']:
|
||||
output[part] = self.get_content(template, lang, part, data)
|
||||
return output
|
||||
|
||||
def get_message(
|
||||
self, template: str, lang: str, sender: str, recipient: str, data: Dict
|
||||
) -> MIMEMultipart:
|
||||
output = self.get_all_contents(template, lang, data)
|
||||
message = EmailMessage(
|
||||
sender,
|
||||
recipient,
|
||||
output['subject.txt'],
|
||||
output['body.html'],
|
||||
output['body.txt'],
|
||||
)
|
||||
return message.generate_message()
|
||||
|
||||
|
||||
class Email:
|
||||
def __init__(self, app: Optional[Flask] = None) -> None:
|
||||
self.host = 'localhost'
|
||||
self.port = 1025
|
||||
self.use_tls = False
|
||||
self.use_ssl = False
|
||||
self.username = None
|
||||
self.password = None
|
||||
self.sender_email = 'no-reply@example.com'
|
||||
self.email_template: Optional[EmailTemplate] = None
|
||||
if app is not None:
|
||||
self.init_email(app)
|
||||
|
||||
def init_email(self, app: Flask) -> None:
|
||||
parsed_url = parse_email_url(app.config['EMAIL_URL'])
|
||||
self.host = parsed_url['host']
|
||||
self.port = parsed_url['port']
|
||||
self.use_tls = parsed_url['use_tls']
|
||||
self.use_ssl = parsed_url['use_ssl']
|
||||
self.username = parsed_url['username']
|
||||
self.password = parsed_url['password']
|
||||
self.sender_email = app.config['SENDER_EMAIL']
|
||||
self.email_template = EmailTemplate(app.config['TEMPLATES_FOLDER'])
|
||||
|
||||
@property
|
||||
def smtp(self) -> Type[Union[smtplib.SMTP_SSL, smtplib.SMTP]]:
|
||||
return smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP
|
||||
|
||||
def send(
|
||||
self, template: str, lang: str, recipient: str, data: Dict
|
||||
) -> None:
|
||||
if not self.email_template:
|
||||
raise Exception('No email template defined.')
|
||||
message = self.email_template.get_message(
|
||||
template, lang, self.sender_email, recipient, data
|
||||
)
|
||||
connection_params = {}
|
||||
if self.use_ssl or self.use_tls:
|
||||
context = ssl.create_default_context()
|
||||
if self.use_ssl:
|
||||
connection_params.update({'context': context})
|
||||
with self.smtp(
|
||||
self.host, self.port, **connection_params # type: ignore
|
||||
) as smtp:
|
||||
smtp.login(self.username, self.password) # type: ignore
|
||||
if self.use_tls:
|
||||
smtp.starttls(context=context)
|
||||
smtp.sendmail(self.sender_email, recipient, message.as_string())
|
||||
smtp.quit()
|
2
fittrackee/emails/exceptions.py
Normal file
2
fittrackee/emails/exceptions.py
Normal file
@ -0,0 +1,2 @@
|
||||
class InvalidEmailUrlScheme(Exception):
|
||||
...
|
268
fittrackee/emails/templates/password_reset_request/en/body.html
Normal file
268
fittrackee/emails/templates/password_reset_request/en/body.html
Normal file
@ -0,0 +1,268 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="x-apple-disable-message-reformatting" content=""/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Fittrackee - Password reset request</title>
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
|
||||
body {
|
||||
background-color: #F4F4F7;
|
||||
color: #51545E;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3869D4;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
td {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.preheader {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
mso-hide: all;
|
||||
font-size: 1px;
|
||||
line-height: 1px;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body,
|
||||
td,
|
||||
th {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #51545E;
|
||||
margin: .4em 0 1.1875em;
|
||||
font-size: 16px;
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
p.sub {
|
||||
color: #6B6E76;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #3869D4;
|
||||
border-top: 10px solid #3869D4;
|
||||
border-right: 18px solid #3869D4;
|
||||
border-bottom: 10px solid #3869D4;
|
||||
border-left: 18px solid #3869D4;
|
||||
display: inline-block;
|
||||
color: #FFF;
|
||||
text-decoration: none;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
|
||||
-webkit-text-size-adjust: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.button--green {
|
||||
background-color: #22BC66;
|
||||
border-top: 10px solid #22BC66;
|
||||
border-right: 18px solid #22BC66;
|
||||
border-bottom: 10px solid #22BC66;
|
||||
border-left: 18px solid #22BC66;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
|
||||
.email-wrapper {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #F4F4F7;
|
||||
}
|
||||
|
||||
.email-content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.email-masthead {
|
||||
padding: 25px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.email-masthead-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #A8AAAF;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 white;
|
||||
}
|
||||
|
||||
.email-body {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.email-body-inner {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.body-action {
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.body-sub {
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
border-top: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
.content-cell {
|
||||
padding: 35px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.email-body-inner,
|
||||
.email-footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body,
|
||||
.email-body,
|
||||
.email-body-inner,
|
||||
.email-content,
|
||||
.email-wrapper,
|
||||
.email-masthead,
|
||||
.email-footer {
|
||||
background-color: #333333 !important;
|
||||
color: #FFF !important;
|
||||
}
|
||||
p,
|
||||
h1 {
|
||||
color: #FFF !important;
|
||||
}
|
||||
.email-masthead-name {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<style type="text/css">
|
||||
.f-fallback {
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<span class="preheader">Use this link to reset your password. The link is only valid for {{ expiration_delay }}.</span>
|
||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="https://example.com" class="f-fallback email-masthead-name">
|
||||
FitTrackee
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="email-body" width="100%" cellpadding="0" cellspacing="0">
|
||||
<table class="email-body-inner" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="content-cell">
|
||||
<div class="f-fallback">
|
||||
<h1>Hi {{username}},</h1>
|
||||
<p>You recently requested to reset your password for your account. Use the button below to reset it.
|
||||
<strong>This password reset link is only valid for {{ expiration_delay }}.</strong>
|
||||
</p>
|
||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="{{password_reset_url}}" class="f-fallback button button--green" target="_blank">Reset your password</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
For security, this request was received from a {{operating_system}} device using {{browser_name}}.
|
||||
If you did not request a password reset, please ignore this email.
|
||||
</p>
|
||||
<p>Thanks,
|
||||
<br>The FitTrackee Team</p>
|
||||
<table class="body-sub" role="presentation">
|
||||
<tr>
|
||||
<td>
|
||||
<p class="f-fallback sub">If you’re having trouble with the button above, copy and paste the URL below into your web browser.</p>
|
||||
<p class="f-fallback sub">{{password_reset_url}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="content-cell" align="center">
|
||||
<p class="f-fallback sub align-center">© FitTrackee.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,10 @@
|
||||
Hi {{username}},
|
||||
|
||||
You recently requested to reset your password for your FitTrackee account. Use the button below to reset it. This password reset link is only valid for {{ expiration_delay }}.
|
||||
|
||||
Reset your password ( {{ password_reset_url }} )
|
||||
|
||||
For security, this request was received from a {{operating_system}} device using {{browser_name}}. If you did not request a password reset, please ignore this email.
|
||||
|
||||
Thanks,
|
||||
The FitTrackee Team
|
@ -0,0 +1 @@
|
||||
FitTrackee - Password reset request
|
269
fittrackee/emails/templates/password_reset_request/fr/body.html
Normal file
269
fittrackee/emails/templates/password_reset_request/fr/body.html
Normal file
@ -0,0 +1,269 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="x-apple-disable-message-reformatting" content=""/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>FitTrackee - Réinitialiser le mot de passe</title>
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
|
||||
body {
|
||||
background-color: #F4F4F7;
|
||||
color: #51545E;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3869D4;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
td {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.preheader {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
mso-hide: all;
|
||||
font-size: 1px;
|
||||
line-height: 1px;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body,
|
||||
td,
|
||||
th {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #51545E;
|
||||
margin: .4em 0 1.1875em;
|
||||
font-size: 16px;
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
p.sub {
|
||||
color: #6B6E76;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #3869D4;
|
||||
border-top: 10px solid #3869D4;
|
||||
border-right: 18px solid #3869D4;
|
||||
border-bottom: 10px solid #3869D4;
|
||||
border-left: 18px solid #3869D4;
|
||||
display: inline-block;
|
||||
color: #FFF;
|
||||
text-decoration: none;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
|
||||
-webkit-text-size-adjust: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.button--green {
|
||||
background-color: #22BC66;
|
||||
border-top: 10px solid #22BC66;
|
||||
border-right: 18px solid #22BC66;
|
||||
border-bottom: 10px solid #22BC66;
|
||||
border-left: 18px solid #22BC66;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
|
||||
.email-wrapper {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #F4F4F7;
|
||||
}
|
||||
|
||||
.email-content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.email-masthead {
|
||||
padding: 25px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.email-masthead-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #A8AAAF;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 white;
|
||||
}
|
||||
|
||||
.email-body {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.email-body-inner {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.body-action {
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.body-sub {
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
border-top: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
.content-cell {
|
||||
padding: 35px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.email-body-inner,
|
||||
.email-footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body,
|
||||
.email-body,
|
||||
.email-body-inner,
|
||||
.email-content,
|
||||
.email-wrapper,
|
||||
.email-masthead,
|
||||
.email-footer {
|
||||
background-color: #333333 !important;
|
||||
color: #FFF !important;
|
||||
}
|
||||
p,
|
||||
h1 {
|
||||
color: #FFF !important;
|
||||
}
|
||||
.email-masthead-name {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<style type="text/css">
|
||||
.f-fallback {
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant {{ expiration_delay }}.</span>
|
||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="https://example.com" class="f-fallback email-masthead-name">
|
||||
FitTrackee
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="email-body" width="100%" cellpadding="0" cellspacing="0">
|
||||
<table class="email-body-inner" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="content-cell">
|
||||
<div class="f-fallback">
|
||||
<h1>Bonjour {{username}},</h1>
|
||||
<p>Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
||||
Cliquez sur le bouton ci-dessous pour le réinitialiser.
|
||||
<strong>Cette réinitialisation n'est valide que pendant {{ expiration_delay }}.</strong>
|
||||
</p>
|
||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="{{password_reset_url}}" class="f-fallback button button--green" target="_blank">Réinitialiser le mot de passe</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Pour vérification, cette demande a été reçue à partir d'un appareil sous {{operating_system}}, utilisant le navigateur {{browser_name}}.
|
||||
Si vous n'avez pas demandé de réinitalisation, vous pouvez ignorer cet e-mail.
|
||||
</p>
|
||||
<p>Merci,
|
||||
<br>L'équipe FitTrackee</p>
|
||||
<table class="body-sub" role="presentation">
|
||||
<tr>
|
||||
<td>
|
||||
<p class="f-fallback sub">Si vous avez des problèmes avec le bouton, vous pouvez copier et coller le lien suivant dans votre navigateur</p>
|
||||
<p class="f-fallback sub">{{password_reset_url}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="content-cell" align="center">
|
||||
<p class="f-fallback sub align-center">© FitTrackee.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,12 @@
|
||||
Bonjour {{username}},
|
||||
|
||||
Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
||||
Cliquez sur le lien ci-dessous pour le réinitialiser. Ce lien n'est valide que pendant {{ expiration_delay }}.
|
||||
|
||||
Réinitialiser le mot de passe: ( {{ password_reset_url }} )
|
||||
|
||||
Pour vérification, cette demande a été reçue à partir d'un appareil sous {{operating_system}}, utilisant le navigateur {{browser_name}}.
|
||||
Si vous n'avez pas demandé de réinitalisation, vous pouvez ignorer cet e-mail.
|
||||
|
||||
Merci,
|
||||
L'équipe FitTrackee
|
@ -0,0 +1 @@
|
||||
FitTrackee - Réinitialiser votre mot de passe
|
20
fittrackee/emails/utils_email.py
Normal file
20
fittrackee/emails/utils_email.py
Normal file
@ -0,0 +1,20 @@
|
||||
from typing import Dict
|
||||
|
||||
from urllib3.util import parse_url
|
||||
|
||||
from .exceptions import InvalidEmailUrlScheme
|
||||
|
||||
|
||||
def parse_email_url(email_url: str) -> Dict:
|
||||
parsed_url = parse_url(email_url)
|
||||
if parsed_url.scheme != 'smtp':
|
||||
raise InvalidEmailUrlScheme()
|
||||
credentials = parsed_url.auth.split(':')
|
||||
return {
|
||||
'host': parsed_url.host,
|
||||
'port': parsed_url.port,
|
||||
'use_tls': True if parsed_url.query == 'tls=True' else False,
|
||||
'use_ssl': True if parsed_url.query == 'ssl=True' else False,
|
||||
'username': credentials[0],
|
||||
'password': credentials[1],
|
||||
}
|
Reference in New Issue
Block a user