Sending emails
Blanq uses vue-email to send emails. The platform used for delivery in production is MailChannels. With MailChannels' free tier for developers you can send up to 3000 emails per month or 100 per day. To save on resources in development there is a helpful Docker Image that you can use to test your email sending.
Setting up the development environment
Clone the mailchannels-dev repository.
git clone --depth=1 https://github.com/Eckhardt-D/mailchannels-dev
Then run the following commands:
cd mailchannels-dev && docker build -t mailchannels-dev .
Start the container:
docker run -d -p 8008:8008 mailchannels-dev
You can now access the MailChannels dashboard at http://localhost:8008. This is where all the emails you send will appear in development. Just be sure to set your .env file correctly:
NUXT_MAIL_CHANNELS_BASE_URL=http://localhost:8008
NUXT_MAIL_CHANNELS_API_KEY=
The docker container API matches the send
endpoint of MailChannels.
Creating an email template
Blanq uses Vue Email to send emails. The email templates are stored in the server/emails
folder. The email templates are written in Vue and can be styled with Tailwind CSS.
<script lang="ts" setup>
import { Button, Text } from '@vue-email/components'
import Base from './base.vue'
defineProps<{
confirmationLink: string
}>()
</script>
<template>
<Base header-text="Please click the button below to confirm your account" title="Verify Your Account">
<Text>If you did not sign up for an account, please ignore this email.</Text>
<Button :href="confirmationLink" class="bg-blue-500 text-white font-bold p-3 rounded">
Verify Account
</Button>
</Base>
</template>
You likely want to use the 'Base' component, and can adjust it to your needs. Find it at server/emails/base.vue
.
Sending an email
There is a useEmail
utils that can be used anywhere in the server to send an email. Here is an example of how to send an email:
const emails = useEmails(useRuntimeConfig(event))
await emails.sendEmailChangeEmail({
newEmail,
resetUrl: url,
to: {
name: user.name,
email: user.email,
},
})
The email util requires the runtimeConfig to read the from email and name and in production the MailChannels API key.
Sending emails in Better Auth's handlers
The flow is a little different in Better Auth's handlers. Since there is no 'event' we cannot reliably get the runtimeConfig to pass to the Email util.
To get around this, there is a special class in utils called EnhancedRequest
. This class is a wrapper around the request object that is passed to Better Auth:
server/api/auth/[...all].ts
export default defineEventHandler((event) => {
const config = useRuntimeConfig(event)
const enhancedRequest = new EnhancedRequest(toWebRequest(event))
enhancedRequest.__config = config
return serverAuth().handler(enhancedRequest)
})
This makes it possible to get the runtime config in the Better Auth handlers:
function createAuth() {
return betterAuth({
...
changeEmail: {
enabled: true,
sendChangeEmailVerification: async ({ user, newEmail, url }, request) => {
if (request === undefined) {
throw new Error('Request is undefined, cannot send email')
}
// This is the request as it is passed from `api/[...auth].ts` which is where
// it is 'enhanced' with the runtimeConfig. Not a perfect solution.
const enhanced: EnhancedRequest = request
const emails = useEmails(enhanced.__config!)
await emails.sendEmailChangeEmail({
newEmail,
resetUrl: url,
to: {
name: user.name,
email: user.email,
},
})
},
},
},
...
Though this is not ideal, for now it's a simple way to access the runtimeConfig in the Better Auth handlers.