Merge pull request #1337 from AppFlowy-IO/magic-link-otp-email

feat: Magic link otp email
This commit is contained in:
Khor Shu Heng 2025-04-14 11:50:08 +08:00 committed by GitHub
commit 18f5244280
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 5155 additions and 16 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 979 B

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

View file

@ -21,16 +21,6 @@
<![endif]-->
<title>Workspace Import Failed</title>
<style>
.p-4 {
padding: 16px
}
.py-4 {
padding-top: 16px;
padding-bottom: 16px
}
.text-white {
color: #fff
}
@media (max-width: 600px) {
.sm-px-4 {
padding-left: 16px !important;

View file

@ -0,0 +1,137 @@
<!DOCTYPE>
<html lang="en" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta charset="utf-8">
<meta name="x-apple-disable-message-reformatting">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">
<!--[if mso]>
<noscript>
<xml>
<o:OfficeDocumentSettings xmlns:o="urn:schemas-microsoft-com:office:office">
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
</noscript>
<style>
td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
</style>
<![endif]-->
<title>Login for AppFlowy</title>
</head>
<body style="margin: 0; width: 100%; padding: 0; -webkit-font-smoothing: antialiased; word-break: break-word">
<div role="article" aria-roledescription="email" aria-label="Login for AppFlowy" lang="en">
<div style="display: none">
To login to AppFlowy, follow this link {{ .ConfirmationURL }}
</div>
<table width="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#eeeefc" style="font-family: Google Sans, Robot, Arial, sans-serif; padding: 24px; color: #000000;" role="presentation">
<tr>
<td align="center" valign="top">
<table width="600" border="0" cellpadding="0" cellspacing="0" bgcolor="#ffffff" style="border-radius: 12px; padding: 32px; margin: 0 auto;" role="presentation">
<tr>
<td align="center" style="padding-bottom: 24px;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/magic-link-otp/assets/mailer_templates/build_production/images/appflowy.png" width="32" height="32" style="max-width: 100%; vertical-align: middle; line-height: 1; border: 0; display: block; margin: 0 auto" alt="">
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 8px;">
<h1 style="font-size: 24px; font-weight: bold; margin: 0; color: #000000;">Login for AppFlowy</h1>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 24px;">
<p style="font-size: 16px; line-height: 1.5; margin: 0;">We received a request to log in to your AppFlowy account.</p>
<p style="font-size: 16px; line-height: 1.5; margin: 2px 0 0;">You can log in using either of the following options:</p>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 12px;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#f8faff" style="border-radius: 20px; padding: 24px 20px;" role="presentation">
<tr>
<td align="center" style="padding-bottom: 16px;">
<h2 style="font-size: 16px; font-weight: bold; margin: 0; color: #000000;">Option 1: Magic Link (Fast &amp; Easy)</h2>
<p style="font-size: 14px; color: #6F748C; margin: 10px 0 0;">Click the button or link below to log in instantly</p>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 16px;">
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto;" role="presentation">
<tr>
<td align="center" bgcolor="#9327ff" style="border-radius: 10px; padding: 10px 16px;">
<a href="{{ .ConfirmationURL }}" style="color: #ffffff; font-weight: 600; font-size: 14px; text-decoration: none; display: inline-block;">Login to AppFlowy</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center">
<p style="padding-bottom: 16px; font-size: 12px; color: #6F748C; margin: 0;">Or paste this into your browser:</p>
<p style="max-width: 384px;margin: 0;">
<a href="{{ .ConfirmationURL }}" style="font-size: 12px; text-decoration: none; text-align: center; color: #6F748C; font-weight: bold; margin: 0; word-break: break-all;">
{{ .ConfirmationURL }}
</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 24px;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#f8faff" style="border-radius: 20px; padding: 24px 20px;" role="presentation">
<tr>
<td align="center" style="padding-bottom: 16px;">
<h2 style="font-size: 16px; font-weight: bold; margin: 0; color: #000000;">Option 2: One-Time Password (OTP)</h2>
<p style="font-size: 14px; color: #6F748C; margin: 10px 0 0;">Prefer to enter a code instead? Use the one-time code below</p>
</td>
</tr>
<tr>
<td align="center">
<span style="background-color: #ffffff; padding: 8px; border-radius: 6px; font-weight: 600; font-size: 16px; display: inline-block;">{{ .Token }}</span>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 24px;">
<p style="font-size: 12px; color: #6F748C; margin: 0;">This code and magic link will expire in 5 minutes for security reasons.</p>
<p style="font-size: 12px; color: #6F748C; margin: 0;">If you didn't initiate this login, you can safely ignore this email. No action is needed.</p>
</td>
</tr>
<tr>
<td align="center">
<p style="border-top: 1px solid #e4e8f5; padding-top: 24px; font-size: 12px; color: #6F748C; margin: 0 0 15px;">Bring projects, knowledge, and teams together with the power of AI.</p>
<p style="margin: 0 0 15px;">
<a href="https://discord.gg/9Q2xaN37tV" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/magic-link-otp/assets/mailer_templates/build_production/images/discord.png" width="20" height="20" alt="Discord" style="max-width: 100%; vertical-align: middle; line-height: 1; border: 0;">
</a>
<a href="https://github.com/AppFlowy-IO/AppFlowy" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/magic-link-otp/assets/mailer_templates/build_production/images/github.png" width="20" height="20" alt="GitHub" style="max-width: 100%; vertical-align: middle; line-height: 1; border: 0;">
</a>
<a href="https://www.reddit.com/r/AppFlowy" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/magic-link-otp/assets/mailer_templates/build_production/images/reddit.png" width="20" height="20" alt="Reddit" style="max-width: 100%; vertical-align: middle; line-height: 1; border: 0;">
</a>
<a href="https://twitter.com/appflowy" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/magic-link-otp/assets/mailer_templates/build_production/images/twitter.png" width="20" height="20" alt="Twitter" style="max-width: 100%; vertical-align: middle; line-height: 1; border: 0;">
</a>
<a href="https://www.youtube.com/@AppFlowyHQ" style="text-decoration: none; display: inline-block;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/magic-link-otp/assets/mailer_templates/build_production/images/youtube.png" width="20" height="20" alt="Youtube" style="max-width: 100%; vertical-align: middle; line-height: 1; border: 0;">
</a>
</p>
<p style="font-size: 12px; color: #6F748C; margin: 0 0 10px;">Copyright © 2025, AppFlowy Inc.</p>
<p style="font-size: 12px; color: #6F748C; margin: 0;">
Need Help? <a href="mailto:support@appflowy.io" style="color: #6F748C;">support@appflowy.io</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</body>
</html>

1
email_template/.npmrc Normal file
View file

@ -0,0 +1 @@
shamefully-hoist=true

View file

@ -7,9 +7,13 @@ AppFlowy Cloud services.
Run this command and follow the prompts
```bash
npm install
# install pnpm@8.5.0
npm run dev
npm install -g pnpm@8.5.0
pnpm i
pnpm run dev
```
## Build
@ -18,5 +22,6 @@ Run this command to build the project to generate the final output in the assets
folder
```bash
npm run build
pnpm run build
```

View file

@ -21,7 +21,7 @@ module.exports = {
},
locals: {
cdnBaseUrl:
"https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/main/assets/mailer_templates/build_production/",
"https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/magic-link-otp/assets/mailer_templates/build_production/",
error: "{{ error }}",
detailError: "{{ error_detail }}",
userIconUrl: "{{ user_icon_url }}",

View file

@ -5,9 +5,14 @@
"build": "maizzle build production"
},
"dependencies": {
"@maizzle/framework": "latest",
"@maizzle/framework": "4.4.6",
"tailwindcss-box-shadow": "^2.0.0",
"tailwindcss-email-variants": "^2.0.0",
"tailwindcss-mso": "^1.4.1"
"tailwindcss-mso": "^1.4.1",
"tailwindcss": "^3.3.0"
},
"engines": {
"pnpm": ">=8.0.0 <9.0.0",
"node": ">=14.0.0"
}
}

4873
email_template/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 979 B

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

View file

@ -0,0 +1,128 @@
---
title: "Login for AppFlowy"
bodyClass: #EEEEFC
---
<x-main>
<div style="display: none">
To login to AppFlowy, follow this link @{{ .ConfirmationURL }}
</div>
<table width="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#EEEEFC" style="font-family: Google Sans, Robot, Arial, sans-serif; padding: 24px; color: #000000;">
<tr>
<td align="center" valign="top">
<table width="600" border="0" cellpadding="0" cellspacing="0" bgcolor="#ffffff" style="border-radius: 12px; padding: 32px; margin: 0 auto;">
<tr>
<td align="center" style="padding-bottom: 24px;">
<img src="{{ cdnBaseUrl }}images/appflowy.png" width="32" height="32" style="border: 0; display: block; margin: 0 auto;" />
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 8px;">
<h1 style="font-size: 24px; font-weight: bold; margin: 0; color: #000000;">Login for AppFlowy</h1>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 24px;">
<p style="font-size: 16px; line-height: 1.5; margin: 0;">We received a request to log in to your AppFlowy account.</p>
<p style="font-size: 16px; line-height: 1.5; margin: 2px 0 0 0;">You can log in using either of the following options:</p>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 12px;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#F8FAFF" style="border-radius: 20px; padding: 24px 20px;">
<tr>
<td align="center" style="padding-bottom: 16px;">
<h2 style="font-size: 16px; font-weight: bold; margin: 0; color: #000000;">Option 1: Magic Link (Fast & Easy)</h2>
<p style="font-size: 14px; color: #6F748C; margin: 10px 0 0 0;">Click the button or link below to log in instantly</p>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 16px;">
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
<tr>
<td align="center" bgcolor="#9327FF" style="border-radius: 10px; padding: 10px 16px;">
<a href="@{{ .ConfirmationURL }}" style="color: #ffffff; font-weight: 600; font-size: 14px; text-decoration: none; display: inline-block;">Login to AppFlowy</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center">
<p style="padding-bottom: 16px; font-size: 12px; color: #6F748C; margin: 0;">Or paste this into your browser:</p>
<p style="max-width: 384px;margin: 0;">
<a href="@{{ .ConfirmationURL }}" style="font-size: 12px; text-decoration: none; text-align: center; color: #6F748C; font-weight: bold; margin: 0; word-break: break-all;">
@{{ .ConfirmationURL }}
</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 24px;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#F8FAFF" style="border-radius: 20px; padding: 24px 20px;">
<tr>
<td align="center" style="padding-bottom: 16px;">
<h2 style="font-size: 16px; font-weight: bold; margin: 0; color: #000000;">Option 2: One-Time Password (OTP)</h2>
<p style="font-size: 14px; color: #6F748C; margin: 10px 0 0 0;">Prefer to enter a code instead? Use the one-time code below</p>
</td>
</tr>
<tr>
<td align="center">
<span style="background-color: #ffffff; padding: 8px; border-radius: 6px; font-weight: 600; font-size: 16px; display: inline-block;">@{{ .Token }}</span>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 24px;">
<p style="font-size: 12px; color: #6F748C; margin: 0;">This code and magic link will expire in 5 minutes for security reasons.</p>
<p style="font-size: 12px; color: #6F748C; margin: 0;">If you didn't initiate this login, you can safely ignore this email. No action is needed.</p>
</td>
</tr>
<tr>
<td align="center">
<p style="border-top: 1px solid #E4E8F5; padding-top: 24px; font-size: 12px; color: #6F748C; margin: 0 0 15px 0;">Bring projects, knowledge, and teams together with the power of AI.</p>
<p style="margin: 0 0 15px 0;">
<a href="https://discord.gg/9Q2xaN37tV" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="{{ cdnBaseUrl }}images/discord.png" width="20" height="20" alt="Discord" style="border: 0;" />
</a>
<a href="https://github.com/AppFlowy-IO/AppFlowy" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="{{ cdnBaseUrl }}images/github.png" width="20" height="20" alt="GitHub" style="border: 0;" />
</a>
<a href="https://www.reddit.com/r/AppFlowy" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="{{ cdnBaseUrl }}images/reddit.png" width="20" height="20" alt="Reddit" style="border: 0;" />
</a>
<a href="https://twitter.com/appflowy" style="text-decoration: none; display: inline-block; margin: 0 10px 0 0;">
<img src="{{ cdnBaseUrl }}images/twitter.png" width="20" height="20" alt="Twitter" style="border: 0;" />
</a>
<a href="https://www.youtube.com/@AppFlowyHQ" style="text-decoration: none; display: inline-block;">
<img src="{{ cdnBaseUrl }}images/youtube.png" width="20" height="20" alt="Youtube" style="border: 0;" />
</a>
</p>
<p style="font-size: 12px; color: #6F748C; margin: 0 0 10px 0;">Copyright © 2025, AppFlowy Inc.</p>
<p style="font-size: 12px; color: #6F748C; margin: 0;">
Need Help? <a href="mailto:support@appflowy.io" style="color: #6F748C;">support@appflowy.io</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</x-main>