使用 React Email 和 Resend 自定义认证邮件
使用 发送邮件钩子 在 Supabase Edge Functions 中通过 React Email 和 Resend 发送自定义认证邮件。
🌐 Use the send email hook to send custom auth emails with React Email and Resend in Supabase Edge Functions.
想直接看代码吗?看看 GitHub 上的示例。
🌐 Prefer to jump straight to the code? Check out the example on GitHub.
先决条件 #
🌐 Prerequisites
要充分利用本指南,你需要:
🌐 To get the most out of this guide, you’ll need to:
确保你已经安装了最新版本的 Supabase CLI。
🌐 Make sure you have the latest version of the Supabase CLI installed.
1. 创建 Supabase 函数 #
🌐 1. Create Supabase function
在本地创建一个新函数:
🌐 Create a new function locally:
supabase functions new send-email2. 编辑处理函数 #
🌐 2. Edit the handler function
把以下代码粘贴到 index.ts 文件里:
🌐 Paste the following code into the index.ts file:
import { renderAsync } from 'npm:@react-email/components@^1'import { withSupabase } from 'npm:@supabase/server@^1'import React from 'npm:react@^19'import { Resend } from 'npm:resend@^6'import { Webhook } from 'npm:standardwebhooks@^1'import { MagicLinkEmail } from './_templates/magic-link.tsx'const resend = new Resend(Deno.env.get('RESEND_API_KEY') as string)const hookSecret = (Deno.env.get('SEND_EMAIL_HOOK_SECRET') as string).replace('v1,whsec_', '')export default { fetch: withSupabase({ auth: 'none' }, async (req) => { if (req.method !== 'POST') { return Response.json({ error: 'not allowed' }, { status: 400 }) } const payload = await req.text() const headers = Object.fromEntries(req.headers) const wh = new Webhook(hookSecret) try { const { user, email_data: { token, token_hash, redirect_to, email_action_type }, } = wh.verify(payload, headers) as { user: { email: string } email_data: { token: string token_hash: string redirect_to: string email_action_type: string site_url: string token_new: string token_hash_new: string } } const html = await renderAsync( React.createElement(MagicLinkEmail, { supabase_url: Deno.env.get('SUPABASE_URL') ?? '', token, token_hash, redirect_to, email_action_type, }) ) const { error } = await resend.emails.send({ from: 'welcome <onboarding@resend.dev>', to: [user.email], subject: 'Supa Custom MagicLink!', html, }) if (error) { throw error } } catch (error) { console.log(error) return Response.json( { error: { http_code: error.code, message: error.message, }, }, { status: 401 } ) } return Response.json({}) }),}3. 创建 React 邮件模板 #
🌐 3. Create React Email templates
按照推荐的项目结构创建一个新的_templates文件夹,并添加一个新的magic-link.tsx文件,内容如下:
🌐 Create a new _templates folder following the recommended project structure and add a new magic-link.tsx file with the following code:
import { Body, Container, Head, Heading, Html, Link, Preview, Text,} from 'npm:@react-email/components@^1'import * as React from 'npm:react@^19'interface MagicLinkEmailProps { supabase_url: string email_action_type: string redirect_to: string token_hash: string token: string}export const MagicLinkEmail = ({ token, supabase_url, email_action_type, redirect_to, token_hash,}: MagicLinkEmailProps) => ( <Html> <Head /> <Preview>Log in with this magic link</Preview> <Body style={main}> <Container style={container}> <Heading style={h1}>Login</Heading> <Link href={`${supabase_url}/auth/v1/verify?token=${token_hash}&type=${email_action_type}&redirect_to=${redirect_to}`} target="_blank" style={{ ...link, display: 'block', marginBottom: '16px', }} > Click here to log in with this magic link </Link> <Text style={{ ...text, marginBottom: '14px' }}> Or, copy and paste this temporary login code: </Text> <code style={code}>{token}</code> <Text style={{ ...text, color: '#ababab', marginTop: '14px', marginBottom: '16px', }} > If you didn't try to login, you can safely ignore this email. </Text> <Text style={footer}> <Link href="https://demo.vercel.store/" target="_blank" style={{ ...link, color: '#898989' }} > ACME Corp </Link> , the famouse demo corp. </Text> </Container> </Body> </Html>)export default MagicLinkEmailconst main = { backgroundColor: '#ffffff',}const container = { paddingLeft: '12px', paddingRight: '12px', margin: '0 auto',}const h1 = { color: '#333', fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", fontSize: '24px', fontWeight: 'bold', margin: '40px 0', padding: '0',}const link = { color: '#2754C5', fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", fontSize: '14px', textDecoration: 'underline',}const text = { color: '#333', fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", fontSize: '14px', margin: '24px 0',}const footer = { color: '#898989', fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", fontSize: '12px', lineHeight: '22px', marginTop: '12px', marginBottom: '24px',}const code = { display: 'inline-block', padding: '16px 4.5%', width: '90.5%', backgroundColor: '#f4f4f4', borderRadius: '5px', border: '1px solid #eee', color: '#333',}你可以在 React Email Examples 找到一些 React 邮件模板。
🌐 You can find a selection of React Email templates in the React Email Examples.
4. 部署函数 #
🌐 4. Deploy the Function
将函数部署到 Supabase:
🌐 Deploy function to Supabase:
supabase functions deploy send-email --no-verify-jwt记下这个函数的 URL,下一步你会用到它!
🌐 Note down the function URL, you will need it in the next step!
5. 配置发送邮件钩子 #
🌐 5. Configure the Send Email Hook
- 去 Supabase 控制面板的 Auth Hooks 部分,创建一个新的“发送邮件钩子”。
- 选择 HTTPS 作为钩子类型。
- 把函数的 URL 粘贴到“URL”字段里。
- 点击“生成密钥”来生成你的 webhook 密钥并记下来。
- 点击“创建”以保存钩子配置。
把这些秘密存到你的 .env 文件里。
🌐 Store these secrets in your .env file.
RESEND_API_KEY=your_resend_api_keySEND_EMAIL_HOOK_SECRET="v1,whsec_<base64_secret>"你可以在 Supabase 仪表板的 Auth Hooks 部分生成密钥。
🌐 You can generate the secret in the Auth Hooks section of the Supabase dashboard.
从 .env 文件中设置秘密:
🌐 Set the secrets from the .env file:
supabase secrets set --env-file supabase/functions/.env现在,每当需要向用户发送认证邮件时,你的 Supabase Edge 函数都会被触发!
🌐 Now your Supabase Edge Function will be triggered anytime an Auth Email needs to be sent to the user!
更多资源 #
🌐 More resources