Your company is expanding to France and Spain. As part of expansion efforts, the company would like to deliver internationalized email templates to best support local users in their native language. Ensure that you have configured `POSTMARK_SERVER_TOKEN` and `AUTH_SEND_EMAIL_HOOK_SECRET` in your `.env` file.
```javascript
import { Webhook } from 'https://esm.sh/standardwebhooks@1.0.0'
import { readAll } from 'https://deno.land/std/io/read_all.ts'
const postmarkEndpoint = 'https://api.postmarkapp.com/email'
// Replace this with your email
const FROM_EMAIL = 'myemail@gmail.com'
// Email Subjects
const subjects = {
en: {
signup: 'Confirm Your Email',
recovery: 'Reset Your Password',
invite: 'You have been invited',
magic_link: 'Your Magic Link',
email_change: 'Confirm Email Change',
email_change_new: 'Confirm New Email Address',
reauthentication: 'Confirm Reauthentication',
},
es: {
signup: 'Confirma tu correo electrónico',
recovery: 'Restablece tu contraseña',
invite: 'Has sido invitado',
magic_link: 'Tu enlace mágico',
email_change: 'Confirma el cambio de correo electrónico',
email_change_new: 'Confirma la Nueva Dirección de Correo',
reauthentication: 'Confirma la reautenticación',
},
fr: {
signup: 'Confirmez votre adresse e-mail',
recovery: 'Réinitialisez votre mot de passe',
invite: 'Vous avez été invité',
magic_link: 'Votre Lien Magique',
email_change: 'Confirmez le changement d’adresse e-mail',
email_change_new: 'Confirmez la nouvelle adresse e-mail',
reauthentication: 'Confirmez la réauthentification',
},
}
// HTML Body
const templates = {
en: {
signup: `Confirm your email
Follow this link to confirm your email:
Confirm your email address
Alternatively, enter the code: {{token}}
`,
recovery: `Reset password
Follow this link to reset the password for your user:
Reset password
Alternatively, enter the code: {{token}}
`,
invite: `You have been invited
You have been invited to create a user on {{site_url}}. Follow this link to accept the invite:
Accept the invite
Alternatively, enter the code: {{token}}
`,
magic_link: `Magic Link
Follow this link to login:
Log In
Alternatively, enter the code: {{token}}
`,
email_change: `Confirm email address change
Follow this link to confirm the update of your email address from {{old_email}} to {{new_email}}:
Change email address
Alternatively, enter the codes: {{token}} and {{new_token}}
`,
email_change_new: `Confirm New Email Address
Follow this link to confirm your new email address:
Confirm new email address
Alternatively, enter the code: {{new_token}}
`,
reauthentication: `Confirm reauthentication
Enter the code: {{token}}
`,
},
es: {
signup: `Confirma tu correo electrónico
Sigue este enlace para confirmar tu correo electrónico:
Confirma tu correo electrónico
Alternativamente, ingresa el código: {{token}}
`,
recovery: `Restablece tu contraseña
Sigue este enlace para restablecer la contraseña de tu usuario:
Restablece tu contraseña
Alternativamente, ingresa el código: {{token}}
`,
invite: `Has sido invitado
Has sido invitado para crear un usuario en {{site_url}}. Sigue este enlace para aceptar la invitación:
Aceptar la invitación
Alternativamente, ingresa el código: {{token}}
`,
magic_link: `Tu enlace mágico
Sigue este enlace para iniciar sesión:
Iniciar sesión
Alternativamente, ingresa el código: {{token}}
`,
email_change: `Confirma el cambio de correo electrónico
Sigue este enlace para confirmar la actualización de tu correo electrónico de {{old_email}} a {{new_email}}:
Cambiar correo electrónico
Alternativamente, ingresa los códigos: {{token}} y {{new_token}}
`,
email_change_new: `Confirma la Nueva Dirección de Correo
Sigue este enlace para confirmar tu nueva dirección de correo electrónico:
Confirma la nueva dirección de correo
Alternativamente, ingresa el código: {{new_token}}
`,
reauthentication: `Confirma la reautenticación
Ingresa el código: {{token}}
`,
},
fr: {
signup: `Confirmez votre adresse e-mail
Suivez ce lien pour confirmer votre adresse e-mail :
Confirmez votre adresse e-mail
Vous pouvez aussi saisir le code : {{token}}
`,
recovery: `Réinitialisez votre mot de passe
Suivez ce lien pour réinitialiser votre mot de passe :
Réinitialisez votre mot de passe
Vous pouvez aussi saisir le code : {{token}}
`,
invite: `Vous avez été invité
Vous avez été invité à créer un utilisateur sur {{site_url}}. Suivez ce lien pour accepter l'invitation :
Acceptez l'invitation
Vous pouvez aussi saisir le code : {{token}}
`,
magic_link: `Votre Lien Magique
Suivez ce lien pour vous connecter :
Connectez-vous
Vous pouvez aussi saisir le code : {{token}}
`,
email_change: `Confirmez le changement d’adresse e-mail
Suivez ce lien pour confirmer la mise à jour de votre adresse e-mail de {{old_email}} à {{new_email}} :
Changez d’adresse e-mail
Vous pouvez aussi saisir les codes : {{token}} et {{new_token}}
`,
email_change_new: `Confirmez la nouvelle adresse e-mail
Suivez ce lien pour confirmer votre nouvelle adresse e-mail :
Confirmez la nouvelle adresse e-mail
Vous pouvez aussi saisir le code : {{new_token}}
`,
reauthentication: `Confirmez la réauthentification
Saisissez le code : {{token}}
`,
},
}
function generateConfirmationURL(email_data) {
// TODO: replace the ref with your project ref
return `https://[.supabase.co/auth/v1/verify?token=${email_data.token}&type=${email_data.email_action_type}&redirect_to=${email_data.redirect_to}`
}
Deno.serve(async (req) => {
const payload = await req.text()
const serverToken = Deno.env.get('POSTMARK_SERVER_TOKEN')
const headers = Object.fromEntries(req.headers)
const base64_secret = Deno.env.get('AUTH_SEND_EMAIL_HOOK_SECRET')
const wh = new Webhook(base64_secret)
const { user, email_data } = wh.verify(payload, headers)
const language = (user.user_metadata && user.user_metadata.i18n) || 'en'
const subject = subjects[language][email_data.email_action_type] || 'Notification'
let template = templates[language][email_data.email_action_type]
const confirmation_url = generateConfirmationURL(email_data)
let htmlBody = template
.replace('{{confirmation_url}}', confirmation_url)
.replace('{{token}}', email_data.token || '')
.replace('{{new_token}}', email_data.new_token || '')
.replace('{{site_url}}', email_data.site_url || '')
.replace('{{old_email}}', email_data.email || '')
.replace('{{new_email}}', email_data.new_email || '')
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'X-Postmark-Server-Token': serverToken,
},
body: JSON.stringify({
From: FROM_EMAIL,
To: user.email,
Subject: subject,
HtmlBody: htmlBody,
}),
}
try {
const response = await fetch(postmarkEndpoint, requestOptions)
if (!response.ok) {
const errorData = await response.json()
throw new Error(`Failed to send email: ${errorData.Message}`)
}
return new Response(
JSON.stringify({
message: 'Email sent successfully.',
}),
{
headers: {
'Content-Type': 'application/json',
},
}
)
} catch (error) {
return new Response(
JSON.stringify({
error: `Failed to process the request: ${error.message}`,
}),
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
}
)
}
})
```
]
{/*
Your company is rapidly growing and depends heavily on email signups. You'd like to configure a backup email provider in case the email provider runs out of credits during your new product launch. Postmark and Sengrid are used as examples but in practice you can use any email provider.
Ensure that you have configured `POSTMARK_SERVER_TOKEN`, `SENDGRID_API_KEY` and `AUTH_SEND_EMAIL_HOOK_SECRET` in your `.env` file.
```javascript
import {
Webhook
} from "https://esm.sh/standardwebhooks@1.0.0";
import {
readAll
} from "https://deno.land/std/io/read_all.ts";
const postmarkEndpoint = 'https://api.postmarkapp.com/email';
const sendGridEndpoint = 'https://api.sendgrid.com/v3/mail/send';
const FROM_EMAIL = 'myemail@gmail.com'
// Email Subjects
const subjects = {
signup: 'Confirm Your Email',
recovery: 'Reset Your Password',
invite: 'You have been invited',
magic_link: 'Your Magic Link',
email_change: 'Confirm Email Change',
email_change_new: 'Confirm New Email Address',
reauthentication: 'Confirm Reauthentication'
};
// HTML Body
const templates = {
signup: `Confirm your email
Follow this link to confirm your email:
Confirm your email address
Alternatively, enter the code: {{token}}
`,
recovery: `Reset password
Follow this link to reset the password for your user:
Reset password
Alternatively, enter the code: {{token}}
`,
invite: `You have been invited
You have been invited to create a user on {{site_url}}. Follow this link to accept the invite:
Accept the invite
Alternatively, enter the code: {{token}}
`,
magic_link: `Magic Link
Follow this link to login:
Log In
Alternatively, enter the code: {{token}}
`,
email_change: `Confirm email address change
Follow this link to confirm the update of your email address from {{old_email}} to {{new_email}}:
Change email address
Alternatively, enter the codes: {{token}} and {{new_token}}
`,
email_change_new: `Confirm New Email Address
Follow this link to confirm your new email address:
Confirm new email address
Alternatively, enter the code: {{new_token}}
`,
reauthentication: `Confirm reauthentication
Enter the code: {{token}}
`
};
function generateConfirmationURL(email_data) {
// TODO: replace the ref with your project ref
return `https://[.supabase.co/auth/v1/verify?token=${email_data.token}&type=${email_data.email_action_type}&redirect_to=${email_data.redirect_to}`
}
async function sendEmailWithPostmark(user: any, email_data: any, serverToken: string): Promise {
const subject = subjects[email_data.email_action_type] || 'Notification';
const confirmation_url = generateConfirmationURL(email_data)
let template = templates[email_data.email_action_type];
let htmlBody = template.replace('{{confirmation_url}}', confirmation_url)
.replace('{{token}}', email_data.token || '')
.replace('{{new_token}}', email_data.new_token || '')
.replace('{{site_url}}', email_data.site_url || '')
.replace('{{old_email}}', email_data.email || '')
.replace('{{new_email}}', email_data.new_email || '');
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Postmark-Server-Token': serverToken
},
body: JSON.stringify({
From: FROM_EMAIL,
To: user.email,
Subject: subject,
HtmlBody: htmlBody
})
};
return await fetch(postmarkEndpoint, requestOptions);
}
async function sendEmailWithSendGrid(user: any, email_data: any, apiKey: string): Promise {
const subject = subjects[email_data.email_action_type] || 'Notification';
let template = templates[email_data.email_action_type];
cont confirmation_url = generateConfirmationURL(email_data)
let htmlBody = template.replace('{{confirmation_url}}', confirmation_url)
.replace('{{token}}', email_data.token || '')
.replace('{{new_token}}', email_data.new_token || '')
.replace('{{site_url}}', email_data.site_url || '')
.replace('{{old_email}}', email_data.email || '')
.replace('{{new_email}}', email_data.new_email || '');
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
personalizations: [{
to: [{
email: user.email
}],
subject: subject
}],
from: {
email: FROM_EMAIL
},
content: [{
type: "text/html",
value: htmlBody
}]
})
};
return await fetch(sendGridEndpoint, requestOptions);
}
Deno.serve(async (req) => {
const payload = await req.text();
const postmarkServerToken = Deno.env.get("POSTMARK_SERVER_TOKEN");
const sendGridApiKey = Deno.env.get("SENDGRID_API_KEY");
const headers = Object.fromEntries(req.headers);
const base64_secret = Deno.env.get('AUTH_SEND_EMAIL_HOOK_SECRET');
const wh = new Webhook(base64_secret);
const {
user,
email_data
} = wh.verify(payload, headers);
try {
// Try sending email using Postmark
let response = await sendEmailWithPostmark(user, email_data, postmarkServerToken!);
if (!response.ok) {
// If Postmark fails, try SendGrid
console.error(`Primary email send failed: ${await response.text()}`);
response = await sendEmailWithSendGrid(user, email_data, sendGridApiKey!);
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Failed to send email via backup: ${errorData.errors[0].message}`);
}
}
return new Response(JSON.stringify({
message: "Email sent successfully."
}), {
headers: {
"Content-Type": "application/json"
}
});
} catch (error) {
return new Response(JSON.stringify({
error: `Failed to process the request: ${error.message}`
}), {
status: 500,
headers: {
"Content-Type": "application/json"
}
});
}
});
```
] */}