diff --git a/examples/edge-functions/supabase/functions/auth-hook-react-email-resend/_templates/sign-up.tsx b/examples/edge-functions/supabase/functions/auth-hook-react-email-resend/_templates/sign-up.tsx new file mode 100644 index 00000000000..6a02f9f1582 --- /dev/null +++ b/examples/edge-functions/supabase/functions/auth-hook-react-email-resend/_templates/sign-up.tsx @@ -0,0 +1,233 @@ +import { + Body, + Container, + Column, + Head, + Heading, + Html, + Img, + Link, + Preview, + Row, + Section, + Text, +} from 'npm:@react-email/components@0.0.22' +import * as React from 'npm:react@18.3.1' + +interface SignUpEmailProps { + username: string + lang: string + token: string + supabase_url: string + email_action_type: string + redirect_to: string + token_hash: string +} + +/** Translations of the text for English */ +const translationsEn = { + confirm_email_address: 'Confirm your email address', + h1: (username: string) => `Hi ${username}! Confirm your email address`, + your_confirmation_code: + 'Thank you for signing up for Story. Please complete the email confirmation for full access.', + click_here: 'Click here to confirm your email address', + copy_and_paste: 'Or, copy and paste this temporary login code:', + if_you_did_not_request: + 'If you did not request this email, there is nothing to worry about, you can safely ignore it.', + blog: 'Our blog', + policies: 'Policies', + help_center: 'Help center', + community: 'Community', +} + +/** Translations of the text for Japanese */ +const translationsJa = { + confirm_email_address: 'メールアドレスの確認', + h1: (username: string) => `${username}様ようこそ!メールアドレスの確認をお願いします`, + your_confirmation_code: + 'Storyへの登録ありがとうございます。メールアドレスの確認を完了してください。', + click_here: 'こちらをクリックしてメールアドレスの確認を完了させてください', + copy_and_paste: 'それかこちらのログインコードを使ってWebサイトで認証を完了させてください。', + if_you_did_not_request: + 'もしメールのリクエストをしていない場合は、無視していただいて構いません。', + blog: 'ブログ', + policies: 'ポリシー', + help_center: 'ヘルプセンター', + community: 'コミュニティ', +} + +export const SignUpEmail = ({ + username, + lang, + token, + supabase_url, + email_action_type, + redirect_to, + token_hash, +}: SignUpEmailProps) => { + const translations = lang.includes('ja') ? translationsJa : translationsEn + + return ( + + + {translations.confirm_email_address} + + +
+ Story +
+ {translations.h1(username)} + {translations.your_confirmation_code} + + + {translations.click_here} + + {translations.copy_and_paste} + +
+ {token} +
+ + {translations.if_you_did_not_request} + +
+ + + Story + + +
+ +
+ + {translations.blog} + +    |    + + {translations.policies} + +    |    + + {translations.help_center} + +    |    + + {translations.community} + +
+
+ + + ) +} + +SignUpEmail.PreviewProps = { + username: 'dshukertjr', + token: '123456', + supabase_url: 'https://123.supabase.co', + email_action_type: 'confirm', + redirect_to: 'https://dshukertjr.dev', + token_hash: '123456', +} as SignUpEmailProps + +export default SignUpEmail + +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 footerLink = { + color: '#ffffff', + textDecoration: 'underline', +} + +const footerLogos = { + marginBottom: '32px', + paddingLeft: '8px', + paddingRight: '8px', + width: '100%', +} + +const main = { + backgroundColor: '#fce7f3', + margin: '0 auto', + fontFamily: + "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", +} + +const container = { + margin: '0 auto', + padding: '0px 20px', +} + +const logoContainer = { + marginTop: '32px', +} + +const h1 = { + color: '#9333ea', + fontSize: '36px', + fontWeight: '700', + margin: '30px 0', + padding: '0', + lineHeight: '42px', +} + +const heroText = { + fontSize: '20px', + lineHeight: '28px', + marginBottom: '30px', +} + +const codeBox = { + background: 'rgb(245, 244, 245)', + borderRadius: '4px', + marginBottom: '30px', + padding: '40px 10px', +} + +const confirmationCodeText = { + fontSize: '30px', + textAlign: 'center' as const, + verticalAlign: 'middle', +} + +const text = { + color: '#000', + fontSize: '14px', + lineHeight: '24px', +} diff --git a/examples/edge-functions/supabase/functions/auth-hook-react-email-resend/index.ts b/examples/edge-functions/supabase/functions/auth-hook-react-email-resend/index.ts index 431245c3e58..8f66c093e91 100644 --- a/examples/edge-functions/supabase/functions/auth-hook-react-email-resend/index.ts +++ b/examples/edge-functions/supabase/functions/auth-hook-react-email-resend/index.ts @@ -3,6 +3,7 @@ import { Webhook } from 'https://esm.sh/standardwebhooks@1.0.0' import { Resend } from 'npm:resend@4.0.0' import { renderAsync } from 'npm:@react-email/components@0.0.22' import { MagicLinkEmail } from './_templates/magic-link.tsx' +import { SignUpEmail } from './_templates/sign-up.tsx' const resend = new Resend(Deno.env.get('RESEND_API_KEY') as string) const hookSecret = Deno.env.get('SEND_EMAIL_HOOK_SECRET') as string @@ -22,6 +23,10 @@ Deno.serve(async (req) => { } = wh.verify(payload, headers) as { user: { email: string + user_metadata: { + username: string + lang: string + } } email_data: { token: string @@ -34,15 +39,34 @@ Deno.serve(async (req) => { } } - const html = await renderAsync( - React.createElement(MagicLinkEmail, { - supabase_url: Deno.env.get('SUPABASE_URL') ?? '', - token, - token_hash, - redirect_to, - email_action_type, - }) - ) + let html: string + + if (email_action_type === 'signup') { + html = await renderAsync( + React.createElement(SignUpEmail, { + username: user['user_metadata'].username, + lang: user['user_metadata'].lang, + supabase_url: Deno.env.get('SUPABASE_URL') ?? '', + token, + token_hash, + redirect_to, + email_action_type, + }) + ) + } else if (email_action_type == 'login') { + html = await renderAsync( + React.createElement(MagicLinkEmail, { + supabase_url: Deno.env.get('SUPABASE_URL') ?? '', + token, + token_hash, + redirect_to, + email_action_type, + }) + ) + } else { + // TODO implement reset_password + throw new Error('Reset Password not implemented') + } const { error } = await resend.emails.send({ from: 'welcome ',