- eslint 迁移至 v9 flat config + eslint-config-next 16,lint 脚本改为 eslint .(next lint 在 Next 16 已移除),新增 typecheck 脚本 - React Compiler 严格规则对存量代码降为警告,修复邮件模板未转义字符 - .env.example 补齐 RESEND_VERIFIED_DOMAIN / RESEND_FROM_NAME / NEXT_PUBLIC_APP_NAME / CONTACT_RECEIVER_EMAIL - email.ts 发件地址改为发送时求值,避免冷启动缓存错误值 - 联系表单接入 server action:留言落库 contact_message,并邮件通知管理员(HTML 转义防注入) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
116 lines
2.3 KiB
TypeScript
116 lines
2.3 KiB
TypeScript
import {
|
|
Body,
|
|
Button,
|
|
Container,
|
|
Head,
|
|
Heading,
|
|
Html,
|
|
Link,
|
|
Preview,
|
|
Section,
|
|
Text,
|
|
} from '@react-email/components';
|
|
import * as React from 'react';
|
|
|
|
interface VerificationEmailProps {
|
|
verificationUrl: string;
|
|
userEmail?: string;
|
|
}
|
|
|
|
export const VerificationEmail = ({
|
|
verificationUrl,
|
|
userEmail,
|
|
}: VerificationEmailProps) => {
|
|
return (
|
|
<Html>
|
|
<Head />
|
|
<Preview>Verify your email address for Toolso.AI</Preview>
|
|
<Body style={main}>
|
|
<Container style={container}>
|
|
<Heading style={h1}>Welcome to Toolso.AI!</Heading>
|
|
|
|
<Text style={text}>
|
|
Thanks for signing up! Please verify your email address to get started.
|
|
</Text>
|
|
|
|
<Section style={buttonContainer}>
|
|
<Button style={button} href={verificationUrl}>
|
|
Verify Email Address
|
|
</Button>
|
|
</Section>
|
|
|
|
<Text style={text}>
|
|
Or copy and paste this URL into your browser:
|
|
</Text>
|
|
|
|
<Link href={verificationUrl} style={link}>
|
|
{verificationUrl}
|
|
</Link>
|
|
|
|
<Text style={footer}>
|
|
If you didn't sign up for Toolso.AI, you can safely ignore this email.
|
|
</Text>
|
|
</Container>
|
|
</Body>
|
|
</Html>
|
|
);
|
|
};
|
|
|
|
export default VerificationEmail;
|
|
|
|
const main = {
|
|
backgroundColor: '#ffffff',
|
|
fontFamily:
|
|
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
|
|
};
|
|
|
|
const container = {
|
|
margin: '0 auto',
|
|
padding: '20px 0 48px',
|
|
width: '560px',
|
|
};
|
|
|
|
const h1 = {
|
|
color: '#333',
|
|
fontSize: '24px',
|
|
fontWeight: '600',
|
|
lineHeight: '40px',
|
|
margin: '0 0 20px',
|
|
};
|
|
|
|
const text = {
|
|
color: '#333',
|
|
fontSize: '14px',
|
|
lineHeight: '24px',
|
|
margin: '0 0 10px',
|
|
};
|
|
|
|
const button = {
|
|
backgroundColor: '#000',
|
|
borderRadius: '6px',
|
|
color: '#fff',
|
|
fontSize: '14px',
|
|
fontWeight: '600',
|
|
textDecoration: 'none',
|
|
textAlign: 'center' as const,
|
|
display: 'block',
|
|
padding: '12px 20px',
|
|
};
|
|
|
|
const buttonContainer = {
|
|
margin: '27px 0',
|
|
};
|
|
|
|
const link = {
|
|
color: '#2754C5',
|
|
fontSize: '14px',
|
|
textDecoration: 'underline',
|
|
wordBreak: 'break-all' as const,
|
|
};
|
|
|
|
const footer = {
|
|
color: '#898989',
|
|
fontSize: '12px',
|
|
lineHeight: '20px',
|
|
margin: '30px 0 0',
|
|
}; |