refactor(cors): 简化CORS配置并移除未使用的参数

feat(email): 提取邮箱验证逻辑到共享模块并增强验证

docs: 更新文档移除不再使用的ALLOW_ORIGIN配置

style(admin): 为设置页面添加加载状态和样式优化

fix(comments): 在邮件发送前增加邮箱格式验证
This commit is contained in:
anghunk
2026-01-19 17:02:19 +08:00
parent e6e21dff61
commit 73bbe44fbc
9 changed files with 217 additions and 118 deletions

View File

@@ -1,9 +1,6 @@
import { Context } from 'hono';
import { Bindings } from '../../bindings';
function isValidEmail(email: string) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
import { isValidEmail } from '../../utils/email';
export const setAdminEmail = async (c: Context<{ Bindings: Bindings }>) => {
try {

View File

@@ -1,7 +1,7 @@
import { Context } from 'hono';
import { UAParser } from 'ua-parser-js';
import { Bindings } from '../../bindings';
import { sendCommentNotification, sendCommentReplyNotification } from '../../utils/email';
import { sendCommentNotification, sendCommentReplyNotification, isValidEmail } from '../../utils/email';
// 检查内容,将<script>标签之间的内容删除
export function checkContent(content: string): string {
@@ -26,6 +26,9 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
if (!email || typeof email !== 'string') {
return c.json({ message: '邮箱不能为空' }, 400);
}
if (!isValidEmail(email)) {
return c.json({ message: '邮箱格式不正确' }, 400);
}
const userAgent = c.req.header('user-agent') || "";
// 1. 获取 IP (Worker 获取 IP 的标准方式)
@@ -122,7 +125,8 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
"SELECT created_at FROM EmailLog WHERE recipient = ? AND type = 'user-reply' ORDER BY created_at DESC LIMIT 1"
).bind(parentComment.email).first<{ created_at: string }>();
const canSendUserMail = !recentUserMail || (Date.now() - new Date(recentUserMail.created_at).getTime() > 60 * 1000);
if (canSendUserMail) {
if (canSendUserMail && isValidEmail(parentComment.email)) {
console.log('PostComment:mailDispatch:userReply:send', {
toEmail: parentComment.email,
toName: parentComment.author

View File

@@ -1,6 +1,6 @@
import { cors } from 'hono/cors'
export const customCors = (_allowOriginStr: string | undefined) => {
export const customCors = () => {
return cors({
origin: '*',
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],

View File

@@ -1,5 +1,9 @@
import { Bindings } from '../bindings';
export function isValidEmail(email: string) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
/**
* 回复通知邮件
*/
@@ -54,6 +58,11 @@ export async function sendCommentReplyNotification(
throw new Error('未配置邮件发送绑定或发件人地址');
}
if (!isValidEmail(toEmail)) {
console.warn('EmailReplyNotification:invalidRecipient', { toEmail });
return;
}
await env.SEND_EMAIL.send({
to: [{ email: toEmail }],
from: { email: env.CF_FROM_EMAIL },
@@ -106,6 +115,11 @@ export async function sendCommentNotification(
throw new Error('未配置邮件发送绑定或发件人地址');
}
if (!isValidEmail(toEmail)) {
console.warn('EmailAdminNotification:invalidRecipient', { toEmail });
return;
}
await env.SEND_EMAIL.send({
to: [{ email: toEmail }],
from: { email: env.CF_FROM_EMAIL },
@@ -125,18 +139,21 @@ async function getAdminNotifyEmail(env: Bindings): Promise<string> {
const row = await env.CWD_DB.prepare('SELECT value FROM Settings WHERE key = ?')
.bind('admin_notify_email')
.first<{ value: string }>();
if (row?.value) {
if (row?.value && isValidEmail(row.value)) {
console.log('EmailAdminNotification:useDbEmail', {
email: row.value
});
return row.value;
}
if (env.EMAIL_ADDRESS) {
if (env.EMAIL_ADDRESS && isValidEmail(env.EMAIL_ADDRESS)) {
console.log('EmailAdminNotification:useEnvEmail', {
email: env.EMAIL_ADDRESS
});
return env.EMAIL_ADDRESS;
}
console.error('EmailAdminNotification:noAdminEmail');
throw new Error('未配置管理员通知邮箱');
console.error('EmailAdminNotification:noAdminEmail', {
dbValue: row?.value,
envValue: env.EMAIL_ADDRESS
});
throw new Error('未配置管理员通知邮箱或格式不正确');
}