Fix template rendering and use verify email template
email html is weird, some stuff isn't supported.
This commit is contained in:
parent
f337f2e785
commit
689b710c9e
@ -4,39 +4,25 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||||
<title>Verify {instanceName} Login from New Location</title>
|
<title>Verify {instanceName} Login from New Location</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
|
||||||
body {
|
|
||||||
color: white;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
background-color: #202225;
|
|
||||||
}
|
}
|
||||||
.btn {
|
p {
|
||||||
font-size: 15px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
|
||||||
padding: 15px 19px;
|
|
||||||
background-color: #ff5f00;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 0 10px rgba(255, 61, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
.btn:hover {
|
.ExternalClass {
|
||||||
background-color: hsl(22.4, 80%, 50%);
|
width: 100%;
|
||||||
}
|
|
||||||
.btn:active {
|
|
||||||
background-color: hsl(22.4, 60%, 50%);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div style="background-color: #202225;">
|
||||||
<img
|
<img
|
||||||
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
||||||
alt="Branding"
|
alt="Branding"
|
||||||
@ -54,7 +40,7 @@
|
|||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 40px 50px;
|
padding: 40px 50px;
|
||||||
background-color: rgba(50, 53, 59, 1);
|
background-color: #32353b;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@ -66,39 +52,52 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Hey {username},
|
Hey {userUsername},
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
It looks like someone tried to log into your {instanceName}
|
It looks like someone tried to log into your {instanceName}
|
||||||
account from a new location. If this is you, follow the link
|
account from a new location. If this is you, follow the link
|
||||||
below to authorize logging in from this location on your
|
below to authorize logging in from this location on your
|
||||||
account. If this isn't you, we suggest changing your password as
|
account. If this isn't you, we suggest changing your
|
||||||
soon as possible.
|
password as soon as possible.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>IP Address:</strong> {ip}
|
<strong>IP Address:</strong> {ipAddress}
|
||||||
<br />
|
<br />
|
||||||
<strong>Location:</strong> {location}
|
<strong>Location:</strong> {locationCity}, {locationRegion},
|
||||||
|
{locationCountryName}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
display: flex;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a class="btn" href="{verifyUrl}" target="_blank"
|
<a
|
||||||
|
href="{verifyUrl}"
|
||||||
|
target="_blank"
|
||||||
|
style="
|
||||||
|
font-size: 15px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 15px 19px;
|
||||||
|
background-color: #ff5f00;
|
||||||
|
border-radius: 5px;
|
||||||
|
"
|
||||||
>Verify Login</a
|
>Verify Login</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-bottom: 10px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
@ -109,5 +108,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,21 +4,25 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||||
<title>{instanceName} Password Changed</title>
|
<title>{instanceName} Password Changed</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
|
||||||
body {
|
|
||||||
color: white;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
background-color: #202225;
|
}
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.ExternalClass {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div style="background-color: #202225;">
|
||||||
<img
|
<img
|
||||||
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
||||||
alt="Branding"
|
alt="Branding"
|
||||||
@ -36,7 +40,7 @@
|
|||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 40px 50px;
|
padding: 40px 50px;
|
||||||
background-color: rgba(50, 53, 59, 1);
|
background-color: #32353b;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@ -48,7 +52,7 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Hey {username},
|
Hey {userUsername},
|
||||||
</p>
|
</p>
|
||||||
<p>Your {instanceName} password has been changed.</p>
|
<p>Your {instanceName} password has been changed.</p>
|
||||||
<p>
|
<p>
|
||||||
@ -56,5 +60,6 @@
|
|||||||
password to your {instanceName} account.
|
password to your {instanceName} account.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,39 +4,25 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||||
<title>Password Reset Request for {instanceName}</title>
|
<title>Password Reset Request for {instanceName}</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
|
||||||
body {
|
|
||||||
color: white;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
background-color: #202225;
|
|
||||||
}
|
}
|
||||||
.btn {
|
p {
|
||||||
font-size: 15px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
|
||||||
padding: 15px 19px;
|
|
||||||
background-color: #ff5f00;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 0 10px rgba(255, 61, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
.btn:hover {
|
.ExternalClass {
|
||||||
background-color: hsl(22.4, 80%, 50%);
|
width: 100%;
|
||||||
}
|
|
||||||
.btn:active {
|
|
||||||
background-color: hsl(22.4, 60%, 50%);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div style="background-color: #202225;">
|
||||||
<img
|
<img
|
||||||
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
||||||
alt="Branding"
|
alt="Branding"
|
||||||
@ -54,7 +40,7 @@
|
|||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 40px 50px;
|
padding: 40px 50px;
|
||||||
background-color: rgba(50, 53, 59, 1);
|
background-color: #32353b;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@ -66,34 +52,40 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Hey {username},
|
Hey {userUsername},
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Your {instanceName} password can be reset by clicking the button
|
Your {instanceName} password can be reset by clicking the
|
||||||
below. If you did not request a new password, please ignore this
|
button below. If you did not request a new password, please
|
||||||
email.
|
ignore this email.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
display: flex;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a class="btn" href="{passwordResetUrl}" target="_blank"
|
<a
|
||||||
|
href="{passwordResetUrl}"
|
||||||
|
target="_blank"
|
||||||
|
style="
|
||||||
|
font-size: 15px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 15px 19px;
|
||||||
|
background-color: #ff5f00;
|
||||||
|
border-radius: 5px;
|
||||||
|
"
|
||||||
>Reset Password</a
|
>Reset Password</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div
|
<div style="text-align: center">
|
||||||
style="
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<p>
|
<p>
|
||||||
Alternatively, you can directly paste this link into
|
Alternatively, you can directly paste this link into
|
||||||
your browser:
|
your browser:
|
||||||
@ -104,5 +96,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,21 +4,25 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||||
<title>Phone Removed From {instanceName} Account</title>
|
<title>Phone Removed From {instanceName} Account</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
|
||||||
body {
|
|
||||||
color: white;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
background-color: #202225;
|
}
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.ExternalClass {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div style="background-color: #202225;">
|
||||||
<img
|
<img
|
||||||
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
||||||
alt="Branding"
|
alt="Branding"
|
||||||
@ -36,7 +40,7 @@
|
|||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 40px 50px;
|
padding: 40px 50px;
|
||||||
background-color: rgba(50, 53, 59, 1);
|
background-color: #32353b;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@ -48,7 +52,7 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Hey {username},
|
Hey {userUsername},
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Your phone number ********{phoneNumber} was recently removed
|
Your phone number ********{phoneNumber} was recently removed
|
||||||
@ -60,5 +64,6 @@
|
|||||||
{instanceName} account at a time.
|
{instanceName} account at a time.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,39 +4,25 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||||
<title>Verify Email Address for {instanceName}</title>
|
<title>Verify Email Address for {instanceName}</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
|
||||||
body {
|
|
||||||
color: white;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
background-color: #202225;
|
|
||||||
}
|
}
|
||||||
.btn {
|
p {
|
||||||
font-size: 15px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
|
||||||
padding: 15px 19px;
|
|
||||||
background-color: #ff5f00;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 0 10px rgba(255, 61, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
.btn:hover {
|
.ExternalClass {
|
||||||
background-color: hsl(22.4, 80%, 50%);
|
width: 100%;
|
||||||
}
|
|
||||||
.btn:active {
|
|
||||||
background-color: hsl(22.4, 60%, 50%);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div style="background-color: #202225;">
|
||||||
<img
|
<img
|
||||||
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
src="https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-Wordmark-Orange.svg"
|
||||||
alt="Branding"
|
alt="Branding"
|
||||||
@ -54,7 +40,7 @@
|
|||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 40px 50px;
|
padding: 40px 50px;
|
||||||
background-color: rgba(50, 53, 59, 1);
|
background-color: #32353b;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@ -66,43 +52,51 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
Hey {username},
|
Hey {userUsername},
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Thanks for registering for an account on {instanceName}! Before
|
Thanks for registering for an account on {instanceName}!
|
||||||
we get started, we just need to confirm that this is you. Click
|
Before we get started, we just need to confirm that this is
|
||||||
below to verify your email address:
|
you. Click below to verify your email address:
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
display: flex;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a class="btn" href="{verificationUrl}" target="_blank"
|
<a
|
||||||
|
class="btn"
|
||||||
|
href="{emailVerificationUrl}"
|
||||||
|
target="_blank"
|
||||||
|
style="
|
||||||
|
font-size: 15px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 15px 19px;
|
||||||
|
background-color: #ff5f00;
|
||||||
|
border-radius: 5px;
|
||||||
|
"
|
||||||
>Verify Email</a
|
>Verify Email</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div
|
<div style="text-align: center">
|
||||||
style="
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<p>
|
<p>
|
||||||
Alternatively, you can directly paste this link into
|
Alternatively, you can directly paste this link into
|
||||||
your browser:
|
your browser:
|
||||||
</p>
|
</p>
|
||||||
<a href="{verificationUrl}" target="_blank"
|
<a href="{emailVerificationUrl}" target="_blank"
|
||||||
>{verificationUrl}</a
|
>{emailVerificationUrl}</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -33,7 +33,7 @@ router.post("/", route({}), async (req: Request, res: Response) => {
|
|||||||
throw new HTTPError("User does not have an email address", 400);
|
throw new HTTPError("User does not have an email address", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Email.sendVerificationEmail(req.user_id, user.email)
|
await Email.sendVerificationEmail(user, user.email)
|
||||||
.then((info) => {
|
.then((info) => {
|
||||||
console.log("Message sent: %s", info.messageId);
|
console.log("Message sent: %s", info.messageId);
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
|
@ -386,7 +386,7 @@ export class User extends BaseClass {
|
|||||||
|
|
||||||
// send verification email if users aren't verified by default and we have an email
|
// send verification email if users aren't verified by default and we have an email
|
||||||
if (!Config.get().defaults.user.verified && email) {
|
if (!Config.get().defaults.user.verified && email) {
|
||||||
await Email.sendVerificationEmail(user.id, email)
|
await Email.sendVerificationEmail(user, email)
|
||||||
.then((info) => {
|
.then((info) => {
|
||||||
console.log("Message sent: %s", info.messageId);
|
console.log("Message sent: %s", info.messageId);
|
||||||
})
|
})
|
||||||
|
@ -16,10 +16,14 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
import nodemailer, { Transporter } from "nodemailer";
|
import nodemailer, { Transporter } from "nodemailer";
|
||||||
|
import { User } from "../entities";
|
||||||
import { Config } from "./Config";
|
import { Config } from "./Config";
|
||||||
import { generateToken } from "./Token";
|
import { generateToken } from "./Token";
|
||||||
|
|
||||||
|
const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets");
|
||||||
export const EMAIL_REGEX =
|
export const EMAIL_REGEX =
|
||||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
|
||||||
@ -51,7 +55,20 @@ export function adjustEmail(email?: string): string | undefined {
|
|||||||
export const Email: {
|
export const Email: {
|
||||||
transporter: Transporter | null;
|
transporter: Transporter | null;
|
||||||
init: () => Promise<void>;
|
init: () => Promise<void>;
|
||||||
sendVerificationEmail: (id: string, email: string) => Promise<any>;
|
generateVerificationLink: (id: string, email: string) => Promise<string>;
|
||||||
|
sendVerificationEmail: (user: User, email: string) => Promise<any>;
|
||||||
|
doReplacements: (
|
||||||
|
template: string,
|
||||||
|
user: User,
|
||||||
|
emailVerificationUrl?: string,
|
||||||
|
passwordResetUrl?: string,
|
||||||
|
ipInfo?: {
|
||||||
|
ip: string;
|
||||||
|
city: string;
|
||||||
|
region: string;
|
||||||
|
country_name: string;
|
||||||
|
},
|
||||||
|
) => string;
|
||||||
} = {
|
} = {
|
||||||
transporter: null,
|
transporter: null,
|
||||||
init: async function () {
|
init: async function () {
|
||||||
@ -78,25 +95,109 @@ export const Email: {
|
|||||||
console.log(`[SMTP] Ready`);
|
console.log(`[SMTP] Ready`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sendVerificationEmail: async function (
|
/**
|
||||||
id: string,
|
* Replaces all placeholders in an email template with the correct values
|
||||||
email: string,
|
*/
|
||||||
): Promise<any> {
|
doReplacements: function (
|
||||||
if (!this.transporter) return;
|
template: string,
|
||||||
|
user: User,
|
||||||
|
emailVerificationUrl?: string,
|
||||||
|
passwordResetUrl?: string,
|
||||||
|
ipInfo?: {
|
||||||
|
ip: string;
|
||||||
|
city: string;
|
||||||
|
region: string;
|
||||||
|
country_name: string;
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const { instanceName } = Config.get().general;
|
||||||
|
template = template.replaceAll("{instanceName}", instanceName);
|
||||||
|
template = template.replaceAll("{userUsername}", user.username);
|
||||||
|
template = template.replaceAll(
|
||||||
|
"{userDiscriminator}",
|
||||||
|
user.discriminator,
|
||||||
|
);
|
||||||
|
template = template.replaceAll("{userId}", user.id);
|
||||||
|
if (user.phone)
|
||||||
|
template = template.replaceAll(
|
||||||
|
"{phoneNumber}",
|
||||||
|
user.phone.slice(-4),
|
||||||
|
);
|
||||||
|
if (user.email)
|
||||||
|
template = template.replaceAll("{userEmail}", user.email);
|
||||||
|
|
||||||
|
// template specific replacements
|
||||||
|
if (emailVerificationUrl)
|
||||||
|
template = template.replaceAll(
|
||||||
|
"{emailVerificationUrl}",
|
||||||
|
emailVerificationUrl,
|
||||||
|
);
|
||||||
|
if (passwordResetUrl)
|
||||||
|
template = template.replaceAll(
|
||||||
|
"{passwordResetUrl}",
|
||||||
|
passwordResetUrl,
|
||||||
|
);
|
||||||
|
if (ipInfo) {
|
||||||
|
template = template.replaceAll("{ipAddress}", ipInfo.ip);
|
||||||
|
template = template.replaceAll("{locationCity}", ipInfo.city);
|
||||||
|
template = template.replaceAll("{locationRegion}", ipInfo.region);
|
||||||
|
template = template.replaceAll(
|
||||||
|
"{locationCountryName}",
|
||||||
|
ipInfo.country_name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id user id
|
||||||
|
* @param email user email
|
||||||
|
* @returns a verification link for the user
|
||||||
|
*/
|
||||||
|
generateVerificationLink: async function (id: string, email: string) {
|
||||||
const token = (await generateToken(id, email)) as string;
|
const token = (await generateToken(id, email)) as string;
|
||||||
const instanceUrl =
|
const instanceUrl =
|
||||||
Config.get().general.frontPage || "http://localhost:3001";
|
Config.get().general.frontPage || "http://localhost:3001";
|
||||||
const link = `${instanceUrl}/verify#token=${token}`;
|
const link = `${instanceUrl}/verify#token=${token}`;
|
||||||
|
return link;
|
||||||
|
},
|
||||||
|
sendVerificationEmail: async function (
|
||||||
|
user: User,
|
||||||
|
email: string,
|
||||||
|
): Promise<any> {
|
||||||
|
if (!this.transporter) return;
|
||||||
|
|
||||||
|
// generate a verification link for the user
|
||||||
|
const verificationLink = await this.generateVerificationLink(
|
||||||
|
user.id,
|
||||||
|
email,
|
||||||
|
);
|
||||||
|
// load the email template
|
||||||
|
const rawTemplate = fs.readFileSync(
|
||||||
|
path.join(
|
||||||
|
ASSET_FOLDER_PATH,
|
||||||
|
"email_templates",
|
||||||
|
"verify_email.html",
|
||||||
|
),
|
||||||
|
{ encoding: "utf-8" },
|
||||||
|
);
|
||||||
|
// replace email template placeholders
|
||||||
|
const html = this.doReplacements(rawTemplate, user, verificationLink);
|
||||||
|
|
||||||
|
// extract the title from the email template to use as the email subject
|
||||||
|
const subject = html.match(/<title>(.*)<\/title>/)?.[1] || "";
|
||||||
|
|
||||||
|
// // construct the email
|
||||||
const message = {
|
const message = {
|
||||||
from:
|
from:
|
||||||
Config.get().general.correspondenceEmail || "noreply@localhost",
|
Config.get().general.correspondenceEmail || "noreply@localhost",
|
||||||
to: email,
|
to: email,
|
||||||
subject: `Verify Email Address for ${
|
subject,
|
||||||
Config.get().general.instanceName
|
html,
|
||||||
}`,
|
|
||||||
html: `Please verify your email address by clicking the following link: <a href="${link}">Verify Email</a>`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// // send the email
|
||||||
return this.transporter.sendMail(message);
|
return this.transporter.sendMail(message);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user