mirror of
https://github.com/bitwarden/server.git
synced 2025-07-18 16:11:28 -05:00
[PM-22423] Add MJML (#5941)
Scaffolds MJML and adds some initial templates and components. Of interest are: * src/Core/MailTemplates/Mjml/components/hero.js demonstrates how to create a custom MJML component. In our case it's a hero component with our logo, a title, a call to action button and an image. * src/Core/MailTemplates/Mjml/components/head.mjml defines some common styling. * src/Core/MailTemplates/Mjml/components/footer.mjml social links and footer.
This commit is contained in:
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@ -33,6 +33,9 @@ util/SqliteMigrations/** @bitwarden/dept-dbops
|
|||||||
# Shared util projects
|
# Shared util projects
|
||||||
util/Setup/** @bitwarden/dept-bre @bitwarden/team-platform-dev
|
util/Setup/** @bitwarden/dept-bre @bitwarden/team-platform-dev
|
||||||
|
|
||||||
|
# UIF
|
||||||
|
src/Core/MailTemplates/Mjml @bitwarden/team-ui-foundation # Teams are expected to own sub-directories of this project
|
||||||
|
|
||||||
# Auth team
|
# Auth team
|
||||||
**/Auth @bitwarden/team-auth-dev
|
**/Auth @bitwarden/team-auth-dev
|
||||||
bitwarden_license/src/Sso @bitwarden/team-auth-dev
|
bitwarden_license/src/Sso @bitwarden/team-auth-dev
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -214,6 +214,7 @@ bitwarden_license/src/Sso/wwwroot/assets
|
|||||||
.idea/*
|
.idea/*
|
||||||
**/**.swp
|
**/**.swp
|
||||||
.mono
|
.mono
|
||||||
|
src/Core/MailTemplates/Mjml/out
|
||||||
|
|
||||||
src/Admin/Admin.zip
|
src/Admin/Admin.zip
|
||||||
src/Api/Api.zip
|
src/Api/Api.zip
|
||||||
|
5
src/Core/MailTemplates/Mjml/.mjmlconfig
Normal file
5
src/Core/MailTemplates/Mjml/.mjmlconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
"components/hero"
|
||||||
|
]
|
||||||
|
}
|
19
src/Core/MailTemplates/Mjml/README.md
Normal file
19
src/Core/MailTemplates/Mjml/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Email templates
|
||||||
|
|
||||||
|
This directory contains MJML templates for emails sent by the application. MJML is a markup language designed to reduce the pain of coding responsive email templates.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
|
||||||
|
# Build once
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# To build on changes
|
||||||
|
npm run watch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
MJML supports components and you can create your own components by adding them to `.mjmlconfig`.
|
4
src/Core/MailTemplates/Mjml/build.sh
Executable file
4
src/Core/MailTemplates/Mjml/build.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
# TODO: This should probably be replaced with a node script building every file in `emails/`
|
||||||
|
|
||||||
|
npx mjml emails/invite.mjml -o out/invite.html
|
||||||
|
npx mjml emails/two-factor.mjml -o out/two-factor.html
|
53
src/Core/MailTemplates/Mjml/components/footer.mjml
Normal file
53
src/Core/MailTemplates/Mjml/components/footer.mjml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<mj-section>
|
||||||
|
<mj-column>
|
||||||
|
<mj-social icon-size="30px" inner-padding="10px" padding="0">
|
||||||
|
<mj-social-element
|
||||||
|
href="https://twitter.com/bitwarden"
|
||||||
|
src="https://bitwarden.com/images/mail-twitter.png"
|
||||||
|
></mj-social-element>
|
||||||
|
|
||||||
|
<mj-social-element
|
||||||
|
href="https://www.reddit.com/r/Bitwarden/"
|
||||||
|
src="https://bitwarden.com/images/mail-reddit.png"
|
||||||
|
></mj-social-element>
|
||||||
|
|
||||||
|
<mj-social-element
|
||||||
|
href="https://community.bitwarden.com/"
|
||||||
|
src="https://bitwarden.com/images/mail-discourse.png"
|
||||||
|
></mj-social-element>
|
||||||
|
|
||||||
|
<mj-social-element
|
||||||
|
href="https://github.com/bitwarden"
|
||||||
|
src="https://bitwarden.com/images/mail-github.png"
|
||||||
|
></mj-social-element>
|
||||||
|
|
||||||
|
<mj-social-element
|
||||||
|
href="https://www.youtube.com/channel/UCId9a_jQqvJre0_dE2lE_Rw"
|
||||||
|
src="https://bitwarden.com/images/mail-youtube.png"
|
||||||
|
></mj-social-element>
|
||||||
|
|
||||||
|
<mj-social-element
|
||||||
|
href="https://www.linkedin.com/company/bitwarden1/"
|
||||||
|
src="https://bitwarden.com/images/mail-linkedin.png"
|
||||||
|
></mj-social-element>
|
||||||
|
|
||||||
|
<mj-social-element
|
||||||
|
href="https://www.facebook.com/bitwarden/"
|
||||||
|
src="https://bitwarden.com/images/mail-facebook.png"
|
||||||
|
></mj-social-element>
|
||||||
|
</mj-social>
|
||||||
|
|
||||||
|
<mj-text align="center" font-size="12px" line-height="16px" color="#5A6D91">
|
||||||
|
<p style="margin-bottom: 5px">
|
||||||
|
© 2025 Bitwarden Inc. 1 N. Calle Cesar Chavez, Suite 102, Santa
|
||||||
|
Barbara, CA, USA
|
||||||
|
</p>
|
||||||
|
<p style="margin-top: 5px">
|
||||||
|
Always confirm you are on a trusted Bitwarden domain before logging
|
||||||
|
in:<br />
|
||||||
|
<a href="#">bitwarden.com</a> |
|
||||||
|
<a href="#">Learn why we include this</a>
|
||||||
|
</p>
|
||||||
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
16
src/Core/MailTemplates/Mjml/components/head.mjml
Normal file
16
src/Core/MailTemplates/Mjml/components/head.mjml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<mj-attributes>
|
||||||
|
<mj-all
|
||||||
|
font-family="'Helvetica Neue', Helvetica, Arial, sans-serif"
|
||||||
|
font-size="16px"
|
||||||
|
/>
|
||||||
|
<mj-button background-color="#175ddc" />
|
||||||
|
<mj-text color="#333" />
|
||||||
|
<mj-body background-color="#e6e9ef" width="660px" />
|
||||||
|
</mj-attributes>
|
||||||
|
<mj-style inline="inline">
|
||||||
|
.link { text-decoration: none; color: #175ddc; font-weight: 600 }
|
||||||
|
</mj-style>
|
||||||
|
<mj-style>
|
||||||
|
.border-fix > table { border-collapse:separate !important; } .border-fix >
|
||||||
|
table > tbody > tr > td { border-radius: 3px; }
|
||||||
|
</mj-style>
|
64
src/Core/MailTemplates/Mjml/components/hero.js
Normal file
64
src/Core/MailTemplates/Mjml/components/hero.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
const { BodyComponent } = require("mjml-core");
|
||||||
|
class MjBwHero extends BodyComponent {
|
||||||
|
static dependencies = {
|
||||||
|
// Tell the validator which tags are allowed as our component's parent
|
||||||
|
"mj-column": ["mj-bw-hero"],
|
||||||
|
"mj-wrapper": ["mj-bw-hero"],
|
||||||
|
// Tell the validator which tags are allowed as our component's children
|
||||||
|
"mj-bw-hero": [],
|
||||||
|
};
|
||||||
|
|
||||||
|
static allowedAttributes = {
|
||||||
|
"img-src": "string",
|
||||||
|
title: "string",
|
||||||
|
"button-text": "string",
|
||||||
|
"button-url": "string",
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultAttributes = {};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.renderMJML(`
|
||||||
|
<mj-section
|
||||||
|
full-width="full-width"
|
||||||
|
background-color="#175ddc"
|
||||||
|
border-radius="4px 4px 0 0"
|
||||||
|
>
|
||||||
|
<mj-column width="70%">
|
||||||
|
<mj-image
|
||||||
|
align="left"
|
||||||
|
src="https://bitwarden.com/images/logo-horizontal-white.png"
|
||||||
|
width="150px"
|
||||||
|
height="30px"
|
||||||
|
></mj-image>
|
||||||
|
<mj-text color="#fff" padding-top="0" padding-bottom="0">
|
||||||
|
<h1 style="font-weight: normal; font-size: 24px; line-height: 32px">
|
||||||
|
${this.getAttribute("title")}
|
||||||
|
</h1>
|
||||||
|
</mj-text>
|
||||||
|
<mj-button
|
||||||
|
href="${this.getAttribute("button-url")}"
|
||||||
|
background-color="#fff"
|
||||||
|
color="#1A41AC"
|
||||||
|
border-radius="20px"
|
||||||
|
align="left"
|
||||||
|
>
|
||||||
|
${this.getAttribute("button-text")}
|
||||||
|
</mj-button
|
||||||
|
>
|
||||||
|
</mj-column>
|
||||||
|
<mj-column width="30%" vertical-align="bottom">
|
||||||
|
<mj-image
|
||||||
|
src="${this.getAttribute("img-src")}"
|
||||||
|
alt=""
|
||||||
|
width="140px"
|
||||||
|
height="140px"
|
||||||
|
padding="0"
|
||||||
|
/>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MjBwHero;
|
11
src/Core/MailTemplates/Mjml/components/logo.mjml
Normal file
11
src/Core/MailTemplates/Mjml/components/logo.mjml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<mj-section>
|
||||||
|
<mj-column>
|
||||||
|
<mj-image
|
||||||
|
align="center"
|
||||||
|
padding="10px 25px"
|
||||||
|
src="https://bitwarden.com/images/logo-horizontal-blue.png"
|
||||||
|
width="250px"
|
||||||
|
height="39px"
|
||||||
|
></mj-image>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
49
src/Core/MailTemplates/Mjml/emails/invite.mjml
Normal file
49
src/Core/MailTemplates/Mjml/emails/invite.mjml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<mjml>
|
||||||
|
<mj-head>
|
||||||
|
<mj-include path="../components/head.mjml" />
|
||||||
|
</mj-head>
|
||||||
|
|
||||||
|
<mj-body>
|
||||||
|
<mj-wrapper css-class="border-fix" padding="20px 20px">
|
||||||
|
<mj-bw-hero
|
||||||
|
img-src="https://assets.bitwarden.com/email/v1/business.png"
|
||||||
|
title="A Bitwarden member has invited you to Bitwarden Password Manager"
|
||||||
|
button-text="Finish account setup"
|
||||||
|
button-url="#"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<mj-section>
|
||||||
|
<mj-column>
|
||||||
|
<mj-button href="#">Join Organization Now</mj-button>
|
||||||
|
|
||||||
|
<mj-text>
|
||||||
|
This invitation expires on
|
||||||
|
<b>Tuesday, January 23, 2024 2:59PM UTC</b>.
|
||||||
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
<mj-section background-color="#fbfbfb">
|
||||||
|
<mj-column width="70%">
|
||||||
|
<mj-text line-height="24px">
|
||||||
|
<h3 style="font-size: 20px; margin: 0; line-height: 28px">
|
||||||
|
We’re here for you!
|
||||||
|
</h3>
|
||||||
|
If you have any questions, search the Bitwarden
|
||||||
|
<a href="#" class="link">Help</a>
|
||||||
|
site or
|
||||||
|
<a href="#" class="link">contact us</a>.
|
||||||
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
<mj-column width="30%">
|
||||||
|
<mj-image
|
||||||
|
src="https://assets.bitwarden.com/email/v1/chat.png"
|
||||||
|
height="77px"
|
||||||
|
width="94px"
|
||||||
|
/>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
</mj-wrapper>
|
||||||
|
|
||||||
|
<mj-include path="../components/footer.mjml" />
|
||||||
|
</mj-body>
|
||||||
|
</mjml>
|
27
src/Core/MailTemplates/Mjml/emails/two-factor.mjml
Normal file
27
src/Core/MailTemplates/Mjml/emails/two-factor.mjml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<mjml>
|
||||||
|
<mj-head>
|
||||||
|
<mj-include path="../components/head.mjml" />
|
||||||
|
</mj-head>
|
||||||
|
|
||||||
|
<mj-body background-color="#f6f6f6">
|
||||||
|
<mj-include path="../components/logo.mjml" />
|
||||||
|
|
||||||
|
<mj-wrapper
|
||||||
|
background-color="#fff"
|
||||||
|
border="1px solid #e9e9e9"
|
||||||
|
css-class="border-fix"
|
||||||
|
padding="0"
|
||||||
|
>
|
||||||
|
<mj-section>
|
||||||
|
<mj-column>
|
||||||
|
<mj-text>
|
||||||
|
<p>Your two-step verification code is: <b>{{Token}}</b></p>
|
||||||
|
<p>Use this code to complete logging in with Bitwarden.</p>
|
||||||
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
</mj-wrapper>
|
||||||
|
|
||||||
|
<mj-include path="../components/footer.mjml" />
|
||||||
|
</mj-body>
|
||||||
|
</mjml>
|
2186
src/Core/MailTemplates/Mjml/package-lock.json
generated
Normal file
2186
src/Core/MailTemplates/Mjml/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
src/Core/MailTemplates/Mjml/package.json
Normal file
30
src/Core/MailTemplates/Mjml/package.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "@bitwarden/mjml-emails",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Email templates for Bitwarden",
|
||||||
|
"private": true,
|
||||||
|
"type": "commonjs",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/bitwarden/server.git"
|
||||||
|
},
|
||||||
|
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||||
|
"license": "SEE LICENSE IN LICENSE.txt",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/bitwarden/server/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://bitwarden.com",
|
||||||
|
"scripts": {
|
||||||
|
"build": "./build.sh",
|
||||||
|
"watch": "nodemon --exec ./build.sh --watch ./components --watch ./emails --ext js,mjml",
|
||||||
|
"prettier": "prettier --cache --write ."
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mjml": "4.15.3",
|
||||||
|
"mjml-core": "4.15.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "3.1.10",
|
||||||
|
"prettier": "3.5.3"
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user