mirror of
https://codeberg.org/timelimit/timelimit-server.git
synced 2025-10-03 09:49:32 +02:00
Send mail notifications for new devices and password resets
This commit is contained in:
parent
50c0982bd3
commit
03890f209a
15 changed files with 583 additions and 22 deletions
190
other/mail/device-linked-by-mail/html.ejs
Normal file
190
other/mail/device-linked-by-mail/html.ejs
Normal file
|
@ -0,0 +1,190 @@
|
|||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
</title>
|
||||
<!--[if !mso]><!-->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!--<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
#outlook a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin: 13px 0;
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<noscript>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
</noscript>
|
||||
<![endif]-->
|
||||
<!--[if lte mso 11]>
|
||||
<style type="text/css">
|
||||
.mj-outlook-group-fix { width:100% !important; }
|
||||
</style>
|
||||
<![endif]-->
|
||||
<style type="text/css">
|
||||
@media only screen and (min-width:480px) {
|
||||
.mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style media="screen and (min-width:480px)">
|
||||
.moz-text-html .mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="word-spacing:normal;">
|
||||
<div style="">
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#009688" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="background:#009688;background-color:#009688;margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#009688;background-color:#009688;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:20px;line-height:1;text-align:left;color:#ffffff;">TimeLimit</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
|
||||
<p><%= preText %> <%= deviceName %> <%= postText %></p>
|
||||
<p><%= securityText %></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<p style="border-top:dashed 1px lightgrey;font-size:1px;margin:0px auto;width:100%;">
|
||||
</p>
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:dashed 1px lightgrey;font-size:1px;margin:0px auto;width:550px;" role="presentation" width="550px" ><tr><td style="height:0;line-height:0;">
|
||||
</td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
|
||||
<p> Sie erhalten diese Nachricht, da ein Gerät zu Ihrem Konto hinzugefügt wurde. Mit diesem Gerät können die TimeLimit-Einstellungen ohne ein Passwort geändert werden. Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten. </p>
|
||||
<p> You got this message because a device was added to your account. This device can be used to change the TimeLimit configuration without any password. If you have got any questions, then you can reply to this messagge. </p>
|
||||
<p> © <%= mailimprint %> </p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
41
other/mail/device-linked-by-mail/htmltemplate-src.txt
Normal file
41
other/mail/device-linked-by-mail/htmltemplate-src.txt
Normal file
|
@ -0,0 +1,41 @@
|
|||
<mjml>
|
||||
<mj-body>
|
||||
<mj-section background-color="#009688">
|
||||
<mj-column>
|
||||
<mj-text font-size="20px" color="#ffffff">TimeLimit</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text>
|
||||
<p><%= preText %> <%= deviceName %> <%= postText %></p>
|
||||
<p><%= securityText %></p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-divider border-width="1px" border-style="dashed" border-color="lightgrey" />
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text>
|
||||
<p>
|
||||
Sie erhalten diese Nachricht, da ein Gerät zu Ihrem Konto hinzugefügt wurde.
|
||||
Mit diesem Gerät können die TimeLimit-Einstellungen ohne ein Passwort geändert werden.
|
||||
Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten.
|
||||
</p>
|
||||
<p>
|
||||
You got this message because a device was added to your account.
|
||||
This device can be used to change the TimeLimit configuration without any password.
|
||||
If you have got any questions, then you can reply to this messagge.
|
||||
</p>
|
||||
<p>
|
||||
© <%= mailimprint %>
|
||||
</p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
1
other/mail/device-linked-by-mail/subject.ejs
Normal file
1
other/mail/device-linked-by-mail/subject.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- subject %>
|
15
other/mail/device-linked-by-mail/text.ejs
Normal file
15
other/mail/device-linked-by-mail/text.ejs
Normal file
|
@ -0,0 +1,15 @@
|
|||
<%- preText %> <%- deviceName %> <%- postText %>
|
||||
|
||||
<%- securityText %>
|
||||
|
||||
----------------------
|
||||
|
||||
Sie erhalten diese Nachricht, da ein Gerät zu Ihrem Konto hinzugefügt wurde.
|
||||
Mit diesem Gerät können die TimeLimit-Einstellungen ohne ein Passwort geändert werden.
|
||||
Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten.
|
||||
|
||||
You got this message because a device was added to your account.
|
||||
This device can be used to change the TimeLimit configuration without any password.
|
||||
If you have got any questions, then you can reply to this messagge.
|
||||
|
||||
<C> <%- mailimprint %>
|
190
other/mail/password-recovery-used/html.ejs
Normal file
190
other/mail/password-recovery-used/html.ejs
Normal file
|
@ -0,0 +1,190 @@
|
|||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
</title>
|
||||
<!--[if !mso]><!-->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!--<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
#outlook a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin: 13px 0;
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<noscript>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
</noscript>
|
||||
<![endif]-->
|
||||
<!--[if lte mso 11]>
|
||||
<style type="text/css">
|
||||
.mj-outlook-group-fix { width:100% !important; }
|
||||
</style>
|
||||
<![endif]-->
|
||||
<style type="text/css">
|
||||
@media only screen and (min-width:480px) {
|
||||
.mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style media="screen and (min-width:480px)">
|
||||
.moz-text-html .mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="word-spacing:normal;">
|
||||
<div style="">
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#009688" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="background:#009688;background-color:#009688;margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#009688;background-color:#009688;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:20px;line-height:1;text-align:left;color:#ffffff;">TimeLimit</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
|
||||
<p><%= text %></p>
|
||||
<p><%= securityText %></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<p style="border-top:dashed 1px lightgrey;font-size:1px;margin:0px auto;width:100%;">
|
||||
</p>
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:dashed 1px lightgrey;font-size:1px;margin:0px auto;width:550px;" role="presentation" width="550px" ><tr><td style="height:0;line-height:0;">
|
||||
</td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
|
||||
<p> Sie erhalten diese Nachricht, da Ihr TimeLimit-Passwort geändert wurde. Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten. </p>
|
||||
<p> You got this message because the password reset feature was used for your account. If you have got any questions, then you can reply to this messagge. </p>
|
||||
<p> © <%= mailimprint %> </p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
39
other/mail/password-recovery-used/htmltemplate-src.txt
Normal file
39
other/mail/password-recovery-used/htmltemplate-src.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
<mjml>
|
||||
<mj-body>
|
||||
<mj-section background-color="#009688">
|
||||
<mj-column>
|
||||
<mj-text font-size="20px" color="#ffffff">TimeLimit</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text>
|
||||
<p><%= text %></p>
|
||||
<p><%= securityText %></p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-divider border-width="1px" border-style="dashed" border-color="lightgrey" />
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text>
|
||||
<p>
|
||||
Sie erhalten diese Nachricht, da Ihr TimeLimit-Passwort geändert wurde.
|
||||
Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten.
|
||||
</p>
|
||||
<p>
|
||||
You got this message because the password reset feature was used for your account.
|
||||
If you have got any questions, then you can reply to this messagge.
|
||||
</p>
|
||||
<p>
|
||||
© <%= mailimprint %>
|
||||
</p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
1
other/mail/password-recovery-used/subject.ejs
Normal file
1
other/mail/password-recovery-used/subject.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- subject %>
|
13
other/mail/password-recovery-used/text.ejs
Normal file
13
other/mail/password-recovery-used/text.ejs
Normal file
|
@ -0,0 +1,13 @@
|
|||
<%- text %>
|
||||
|
||||
<%- securityText %>
|
||||
|
||||
----------------------
|
||||
|
||||
Sie erhalten diese Nachricht, da Ihr TimeLimit-Passwort geändert wurde.
|
||||
Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten.
|
||||
|
||||
You got this message because the password reset feature was used for your account.
|
||||
If you have got any questions, then you can reply to this messagge.
|
||||
|
||||
<C> <%- mailimprint %>
|
|
@ -36,7 +36,7 @@ export const createAuthTokenByMailAddress = async ({
|
|||
return token
|
||||
}
|
||||
|
||||
export const getMailByAuthToken = async ({
|
||||
export const getMailAndLocaleByAuthToken = async ({
|
||||
mailAuthToken, database, transaction, invalidate
|
||||
}: {
|
||||
mailAuthToken: string, database: Database, transaction: Transaction, invalidate: boolean
|
||||
|
@ -62,22 +62,25 @@ export const getMailByAuthToken = async ({
|
|||
}
|
||||
}
|
||||
|
||||
return entry.mail
|
||||
return {
|
||||
mail: entry.mail,
|
||||
locale: entry.locale
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const requireMailByAuthToken = async ({
|
||||
export const requireMailAndLocaleByAuthToken = async ({
|
||||
mailAuthToken, database, transaction, invalidate
|
||||
}: {
|
||||
mailAuthToken: string, database: Database, transaction: Transaction, invalidate: boolean
|
||||
}) => {
|
||||
const mail = await getMailByAuthToken({ mailAuthToken, database, transaction, invalidate })
|
||||
const result = await getMailAndLocaleByAuthToken({ mailAuthToken, database, transaction, invalidate })
|
||||
|
||||
if (!mail) {
|
||||
if (!result) {
|
||||
throw new Unauthorized()
|
||||
}
|
||||
|
||||
return mail
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import { maxMailNotificationFlags } from '../../database/user'
|
|||
import {
|
||||
generateAuthToken, generateFamilyId, generateIdWithinFamily, generateVersionId
|
||||
} from '../../util/token'
|
||||
import { requireMailByAuthToken } from '../authentication'
|
||||
import { requireMailAndLocaleByAuthToken } from '../authentication'
|
||||
import { prepareDeviceEntry } from '../device/prepare-device-entry'
|
||||
|
||||
export const createFamily = async ({ database, mailAuthToken, firstParentDevice, password, timeZone, parentName, deviceName }: {
|
||||
|
@ -37,12 +37,12 @@ export const createFamily = async ({ database, mailAuthToken, firstParentDevice,
|
|||
}) => {
|
||||
return database.transaction(async (transaction) => {
|
||||
const now = Date.now().toString(10)
|
||||
const mail = await requireMailByAuthToken({ database, mailAuthToken, transaction, invalidate: true })
|
||||
const mailInfo = await requireMailAndLocaleByAuthToken({ database, mailAuthToken, transaction, invalidate: true })
|
||||
|
||||
// ensure that no family was created for this mail yet
|
||||
const exisitngUserEntry = await database.user.findOne({
|
||||
where: {
|
||||
mail
|
||||
mail: mailInfo.mail
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
@ -77,7 +77,7 @@ export const createFamily = async ({ database, mailAuthToken, firstParentDevice,
|
|||
secondPasswordHash: password.secondHash,
|
||||
secondPasswordSalt: password.secondSalt,
|
||||
type: 'parent',
|
||||
mail,
|
||||
mail: mailInfo.mail,
|
||||
timeZone,
|
||||
disableTimelimitsUntil: '0',
|
||||
currentDevice: '',
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { Database, Transaction } from '../../database'
|
||||
import { StaticMessageException } from '../../exception'
|
||||
import { requireMailByAuthToken } from '../authentication'
|
||||
import { requireMailAndLocaleByAuthToken } from '../authentication'
|
||||
|
||||
const getStatusByMailAddress = async ({
|
||||
mail, database, transaction
|
||||
|
@ -43,7 +43,9 @@ const getStatusByMailAddress = async ({
|
|||
export const getStatusByMailToken = async ({
|
||||
mailAuthToken, database, transaction
|
||||
}: { mailAuthToken: string, database: Database, transaction: Transaction }) => {
|
||||
const mail = await requireMailByAuthToken({ mailAuthToken, database, transaction, invalidate: false })
|
||||
const mailInfo = await requireMailAndLocaleByAuthToken({ mailAuthToken, database, transaction, invalidate: false })
|
||||
const mail = mailInfo.mail
|
||||
|
||||
const status = await getStatusByMailAddress({ mail, database, transaction })
|
||||
|
||||
return { mail, status }
|
||||
|
|
|
@ -19,7 +19,7 @@ import { Conflict, Unauthorized } from 'http-errors'
|
|||
import { Database } from '../../database'
|
||||
import { generateVersionId } from '../../util/token'
|
||||
import { WebsocketApi } from '../../websocket'
|
||||
import { requireMailByAuthToken } from '../authentication'
|
||||
import { requireMailAndLocaleByAuthToken } from '../authentication'
|
||||
import { notifyClientsAboutChangesDelayed } from '../websocket'
|
||||
|
||||
export const linkMailAddress = async ({ mailAuthToken, deviceAuthToken, parentUserId, parentPasswordSecondHash, database, websocket }: {
|
||||
|
@ -45,11 +45,11 @@ export const linkMailAddress = async ({ mailAuthToken, deviceAuthToken, parentUs
|
|||
|
||||
const familyId = deviceEntry.familyId
|
||||
|
||||
const mailAddress = await requireMailByAuthToken({ mailAuthToken, database, transaction, invalidate: true })
|
||||
const mailInfo = await requireMailAndLocaleByAuthToken({ mailAuthToken, database, transaction, invalidate: true })
|
||||
|
||||
const exisitingUser = await database.user.findOne({
|
||||
where: {
|
||||
mail: mailAddress
|
||||
mail: mailInfo.mail
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
@ -83,7 +83,7 @@ export const linkMailAddress = async ({ mailAuthToken, deviceAuthToken, parentUs
|
|||
throw new Conflict()
|
||||
}
|
||||
|
||||
parentEntry.mail = mailAddress
|
||||
parentEntry.mail = mailInfo.mail
|
||||
|
||||
await parentEntry.save({ transaction })
|
||||
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
import { Conflict } from 'http-errors'
|
||||
import { ParentPassword } from '../../api/schema'
|
||||
import { Database } from '../../database'
|
||||
import { sendPasswordRecoveryUsedMail } from '../../util/mail'
|
||||
import { generateVersionId } from '../../util/token'
|
||||
import { WebsocketApi } from '../../websocket'
|
||||
import { requireMailByAuthToken } from '../authentication'
|
||||
import { requireMailAndLocaleByAuthToken } from '../authentication'
|
||||
import { notifyClientsAboutChangesDelayed } from '../websocket'
|
||||
|
||||
export const recoverParentPassword = async ({ database, websocket, password, mailAuthToken }: {
|
||||
|
@ -31,12 +32,12 @@ export const recoverParentPassword = async ({ database, websocket, password, mai
|
|||
// no transaction here because this is directly called from an API endpoint
|
||||
}) => {
|
||||
await database.transaction(async (transaction) => {
|
||||
const mail = await requireMailByAuthToken({ mailAuthToken, database, transaction, invalidate: true })
|
||||
const mailInfo = await requireMailAndLocaleByAuthToken({ mailAuthToken, database, transaction, invalidate: true })
|
||||
|
||||
// update the user entry
|
||||
const userEntry = await database.user.findOne({
|
||||
where: {
|
||||
mail
|
||||
mail: mailInfo.mail
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
@ -69,5 +70,12 @@ export const recoverParentPassword = async ({ database, websocket, password, mai
|
|||
sourceDeviceId: null,
|
||||
transaction
|
||||
})
|
||||
|
||||
transaction.afterCommit(async () => {
|
||||
await sendPasswordRecoveryUsedMail({
|
||||
receiver: mailInfo.mail,
|
||||
locale: mailInfo.locale
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
import { Conflict } from 'http-errors'
|
||||
import { NewDeviceInfo } from '../../api/schema'
|
||||
import { Database } from '../../database'
|
||||
import { sendDeviceLinkedMail } from '../../util/mail'
|
||||
import { generateAuthToken, generateIdWithinFamily, generateVersionId } from '../../util/token'
|
||||
import { WebsocketApi } from '../../websocket'
|
||||
import { requireMailByAuthToken } from '../authentication'
|
||||
import { requireMailAndLocaleByAuthToken } from '../authentication'
|
||||
import { prepareDeviceEntry } from '../device/prepare-device-entry'
|
||||
import { notifyClientsAboutChangesDelayed } from '../websocket'
|
||||
|
||||
|
@ -33,11 +34,11 @@ export const signInIntoFamily = async ({ database, mailAuthToken, newDeviceInfo,
|
|||
// no transaction here because this is directly called from an API endpoint
|
||||
}): Promise<{ deviceId: string; deviceAuthToken: string }> => {
|
||||
return database.transaction(async (transaction) => {
|
||||
const mail = await requireMailByAuthToken({ database, mailAuthToken, transaction, invalidate: true })
|
||||
const mailInfo = await requireMailAndLocaleByAuthToken({ database, mailAuthToken, transaction, invalidate: true })
|
||||
|
||||
const userEntryUnsafe = await database.user.findOne({
|
||||
where: {
|
||||
mail
|
||||
mail: mailInfo.mail
|
||||
},
|
||||
attributes: ['familyId', 'userId'],
|
||||
transaction
|
||||
|
@ -84,6 +85,14 @@ export const signInIntoFamily = async ({ database, mailAuthToken, newDeviceInfo,
|
|||
transaction
|
||||
})
|
||||
|
||||
transaction.afterCommit(async () => {
|
||||
await sendDeviceLinkedMail({
|
||||
receiver: mailInfo.mail,
|
||||
locale: mailInfo.locale,
|
||||
deviceName
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
deviceId,
|
||||
deviceAuthToken
|
||||
|
|
|
@ -108,6 +108,55 @@ export const sendTaskDoneMail = async ({ receiver, child, task }: {
|
|||
})
|
||||
}
|
||||
|
||||
export const sendDeviceLinkedMail = async ({ receiver, deviceName, locale }: {
|
||||
receiver: string
|
||||
deviceName: string
|
||||
locale: string
|
||||
}) => {
|
||||
await email.send({
|
||||
template: join(__dirname, '../../other/mail/device-linked-by-mail'),
|
||||
message: {
|
||||
to: receiver
|
||||
},
|
||||
locals: {
|
||||
subject: locale === 'de' ? 'Gerät hinzugefügt' : 'Device added',
|
||||
preText: locale === 'de' ? 'Soeben wurde das Gerät' : 'The device',
|
||||
deviceName,
|
||||
postText: locale === 'de' ? 'über Ihre E-Mail-Adresse hinzugefügt.' : 'was added using your mail address.',
|
||||
securityText: getMailSecurityText(locale),
|
||||
mailimprint
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const sendPasswordRecoveryUsedMail = async ({ receiver, locale }: {
|
||||
receiver: string
|
||||
locale: string
|
||||
}) => {
|
||||
await email.send({
|
||||
template: join(__dirname, '../../other/mail/password-recovery-used'),
|
||||
message: {
|
||||
to: receiver
|
||||
},
|
||||
locals: {
|
||||
subject: locale === 'de' ? 'Passwort-Vergessen-Funktion verwendet' : 'Password reset',
|
||||
text: locale === 'de' ?
|
||||
'Soeben wurde Ihr TimeLimit-Passwort mit der Passwort-Vergessen-Funktion geändert.' :
|
||||
'Your password was changed using the password reset feature.',
|
||||
securityText: getMailSecurityText(locale),
|
||||
mailimprint
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getMailSecurityText (locale: string) {
|
||||
if (locale === 'de') {
|
||||
return 'Achten Sie darauf, dass Ihr Kind/Ihre Kinder keinen Zugang zu der E-Mail-Adresse hat/haben, die Sie bei TimeLimit angegeben haben.'
|
||||
} else {
|
||||
return 'Make sure that your child/children can not access the mail addresss that you use for TimeLimit.'
|
||||
}
|
||||
}
|
||||
|
||||
export function isMailServerBlacklisted (mail: string): boolean {
|
||||
const parts = mail.split('@')
|
||||
const domain = parts[parts.length - 1]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue