feat(comments): 添加邮箱黑名单功能
实现评论系统的邮箱黑名单功能,包括: 1. 在API中添加邮箱黑名单检查逻辑 2. 在管理后台添加邮箱黑名单设置界面 3. 支持在评论列表中直接屏蔽邮箱 4. 添加相关API接口处理邮箱黑名单操作
This commit is contained in:
@@ -44,6 +44,7 @@ export type CommentSettingsResponse = {
|
|||||||
adminKeySet?: boolean;
|
adminKeySet?: boolean;
|
||||||
requireReview?: boolean;
|
requireReview?: boolean;
|
||||||
blockedIps?: string[];
|
blockedIps?: string[];
|
||||||
|
blockedEmails?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EmailNotifySettingsResponse = {
|
export type EmailNotifySettingsResponse = {
|
||||||
@@ -139,6 +140,7 @@ export function saveCommentSettings(data: {
|
|||||||
adminKey?: string;
|
adminKey?: string;
|
||||||
requireReview?: boolean;
|
requireReview?: boolean;
|
||||||
blockedIps?: string[];
|
blockedIps?: string[];
|
||||||
|
blockedEmails?: string[];
|
||||||
}): Promise<{ message: string }> {
|
}): Promise<{ message: string }> {
|
||||||
return put<{ message: string }>('/admin/settings/comments', data);
|
return put<{ message: string }>('/admin/settings/comments', data);
|
||||||
}
|
}
|
||||||
@@ -147,6 +149,10 @@ export function blockIp(ip: string): Promise<{ message: string }> {
|
|||||||
return post<{ message: string }>('/admin/comments/block-ip', { ip });
|
return post<{ message: string }>('/admin/comments/block-ip', { ip });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function blockEmail(email: string): Promise<{ message: string }> {
|
||||||
|
return post<{ message: string }>('/admin/comments/block-email', { email });
|
||||||
|
}
|
||||||
|
|
||||||
export function exportComments(): Promise<any[]> {
|
export function exportComments(): Promise<any[]> {
|
||||||
return get<any[]>('/admin/comments/export');
|
return get<any[]>('/admin/comments/export');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,15 @@
|
|||||||
/>
|
/>
|
||||||
<div class="cell-author-main">
|
<div class="cell-author-main">
|
||||||
<div class="cell-author-name">{{ item.name }}</div>
|
<div class="cell-author-name">{{ item.name }}</div>
|
||||||
<div class="cell-author-email">{{ item.email }}</div>
|
<div class="cell-author-email">
|
||||||
|
<span
|
||||||
|
class="cell-email-text"
|
||||||
|
@click="handleBlockEmail(item)"
|
||||||
|
title="屏蔽该邮箱"
|
||||||
|
>
|
||||||
|
{{ item.email }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<span class="cell-time">{{ formatDate(item.created) }}</span>
|
<span class="cell-time">{{ formatDate(item.created) }}</span>
|
||||||
<div v-if="item.ipAddress" class="cell-author-ip">
|
<div v-if="item.ipAddress" class="cell-author-ip">
|
||||||
<span class="cell-ip-text" @click="handleBlockIp(item)" title="屏蔽该 IP">{{ item.ipAddress }}</span>
|
<span class="cell-ip-text" @click="handleBlockIp(item)" title="屏蔽该 IP">{{ item.ipAddress }}</span>
|
||||||
@@ -180,6 +188,7 @@ import {
|
|||||||
deleteComment,
|
deleteComment,
|
||||||
updateCommentStatus,
|
updateCommentStatus,
|
||||||
blockIp,
|
blockIp,
|
||||||
|
blockEmail,
|
||||||
} from "../api/admin";
|
} from "../api/admin";
|
||||||
|
|
||||||
const comments = ref<CommentItem[]>([]);
|
const comments = ref<CommentItem[]>([]);
|
||||||
@@ -311,6 +320,21 @@ async function handleBlockIp(item: CommentItem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleBlockEmail(item: CommentItem) {
|
||||||
|
if (!item.email) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!window.confirm(`确认将邮箱 ${item.email} 加入黑名单吗?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await blockEmail(item.email);
|
||||||
|
window.alert(res.message || "已加入邮箱黑名单");
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e.message || "屏蔽邮箱失败";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadComments();
|
loadComments();
|
||||||
});
|
});
|
||||||
@@ -479,6 +503,14 @@ onMounted(() => {
|
|||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cell-email-text {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-email-text:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.cell-content-text {
|
.cell-content-text {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|||||||
@@ -39,27 +39,9 @@
|
|||||||
<input v-model="avatarPrefix" class="form-input" type="text" />
|
<input v-model="avatarPrefix" class="form-input" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<h3 class="card-title">安全设置</h3>
|
<h3 class="card-title">安全设置</h3>
|
||||||
<div class="form-item">
|
|
||||||
<label class="form-label">允许调用的域名(多个域名用逗号分隔,留空则不限制。设置后仅匹配域名可调用前台评论组件。)</label>
|
|
||||||
<textarea
|
|
||||||
v-model="allowedDomains"
|
|
||||||
class="form-input"
|
|
||||||
rows="3"
|
|
||||||
placeholder="例如: example.com, test.com"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-item">
|
|
||||||
<label class="form-label">IP 黑名单(多个 IP 用逗号或换行分隔,留空则不限制)</label>
|
|
||||||
<textarea
|
|
||||||
v-model="blockedIps"
|
|
||||||
class="form-input"
|
|
||||||
rows="3"
|
|
||||||
placeholder="例如: 1.1.1.1, 2.2.2.2"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<label class="form-label">管理员评论密钥</label>
|
<label class="form-label">管理员评论密钥</label>
|
||||||
<div class="form-hint" style="margin-bottom: 4px;">
|
<div class="form-hint" style="margin-bottom: 4px">
|
||||||
设置后前台使用管理员邮箱评论需输入此密钥。
|
设置后前台使用管理员邮箱评论需输入此密钥。
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
@@ -69,6 +51,40 @@
|
|||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label class="form-label"
|
||||||
|
>允许调用的域名(多个域名用逗号分隔,留空则不限制。设置后仅匹配域名可调用前台评论组件。)</label
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
v-model="allowedDomains"
|
||||||
|
class="form-input"
|
||||||
|
rows="3"
|
||||||
|
placeholder="例如: example.com, test.com"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label class="form-label"
|
||||||
|
>IP 黑名单(多个 IP 用逗号或换行分隔,留空则不限制)</label
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
v-model="blockedIps"
|
||||||
|
class="form-input"
|
||||||
|
rows="3"
|
||||||
|
placeholder="例如: 1.1.1.1, 2.2.2.2"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label class="form-label"
|
||||||
|
>邮箱黑名单(多个邮箱用逗号或换行分隔,留空则不限制)</label
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
v-model="blockedEmails"
|
||||||
|
class="form-input"
|
||||||
|
rows="3"
|
||||||
|
placeholder="例如: spam@example.com, bot@test.com"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<button class="card-button" :disabled="savingComment" @click="saveComment">
|
<button class="card-button" :disabled="savingComment" @click="saveComment">
|
||||||
<span v-if="savingComment">保存中...</span>
|
<span v-if="savingComment">保存中...</span>
|
||||||
@@ -205,7 +221,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="card-button secondary"
|
class="card-button secondary"
|
||||||
style="margin-left: auto;"
|
style="margin-left: auto"
|
||||||
@click="resetTemplatesToDefault"
|
@click="resetTemplatesToDefault"
|
||||||
>
|
>
|
||||||
恢复默认模板
|
恢复默认模板
|
||||||
@@ -315,6 +331,7 @@ const avatarPrefix = ref("");
|
|||||||
const commentAdminEnabled = ref(false);
|
const commentAdminEnabled = ref(false);
|
||||||
const allowedDomains = ref("");
|
const allowedDomains = ref("");
|
||||||
const blockedIps = ref("");
|
const blockedIps = ref("");
|
||||||
|
const blockedEmails = ref("");
|
||||||
const commentAdminKey = ref("");
|
const commentAdminKey = ref("");
|
||||||
const adminKeySet = ref(false);
|
const adminKeySet = ref(false);
|
||||||
const requireReview = ref(false);
|
const requireReview = ref(false);
|
||||||
@@ -375,8 +392,9 @@ async function load() {
|
|||||||
allowedDomains.value = commentRes.allowedDomains
|
allowedDomains.value = commentRes.allowedDomains
|
||||||
? commentRes.allowedDomains.join(", ")
|
? commentRes.allowedDomains.join(", ")
|
||||||
: "";
|
: "";
|
||||||
blockedIps.value = commentRes.blockedIps
|
blockedIps.value = commentRes.blockedIps ? commentRes.blockedIps.join(", ") : "";
|
||||||
? commentRes.blockedIps.join(", ")
|
blockedEmails.value = commentRes.blockedEmails
|
||||||
|
? commentRes.blockedEmails.join(", ")
|
||||||
: "";
|
: "";
|
||||||
commentAdminKey.value = commentRes.adminKey || "";
|
commentAdminKey.value = commentRes.adminKey || "";
|
||||||
adminKeySet.value = !!commentRes.adminKeySet;
|
adminKeySet.value = !!commentRes.adminKeySet;
|
||||||
@@ -384,10 +402,8 @@ async function load() {
|
|||||||
emailGlobalEnabled.value = !!emailNotifyRes.globalEnabled;
|
emailGlobalEnabled.value = !!emailNotifyRes.globalEnabled;
|
||||||
|
|
||||||
if (emailNotifyRes.templates) {
|
if (emailNotifyRes.templates) {
|
||||||
templateAdmin.value =
|
templateAdmin.value = emailNotifyRes.templates.admin || DEFAULT_ADMIN_TEMPLATE;
|
||||||
emailNotifyRes.templates.admin || DEFAULT_ADMIN_TEMPLATE;
|
templateReply.value = emailNotifyRes.templates.reply || DEFAULT_REPLY_TEMPLATE;
|
||||||
templateReply.value =
|
|
||||||
emailNotifyRes.templates.reply || DEFAULT_REPLY_TEMPLATE;
|
|
||||||
} else {
|
} else {
|
||||||
templateAdmin.value = DEFAULT_ADMIN_TEMPLATE;
|
templateAdmin.value = DEFAULT_ADMIN_TEMPLATE;
|
||||||
templateReply.value = DEFAULT_REPLY_TEMPLATE;
|
templateReply.value = DEFAULT_REPLY_TEMPLATE;
|
||||||
@@ -515,6 +531,10 @@ async function saveComment() {
|
|||||||
.split(/[,,\n]/)
|
.split(/[,,\n]/)
|
||||||
.map((d) => d.trim())
|
.map((d) => d.trim())
|
||||||
.filter(Boolean),
|
.filter(Boolean),
|
||||||
|
blockedEmails: blockedEmails.value
|
||||||
|
.split(/[,,\n]/)
|
||||||
|
.map((d) => d.trim())
|
||||||
|
.filter(Boolean),
|
||||||
});
|
});
|
||||||
|
|
||||||
showToast(res.message || "保存成功", "success");
|
showToast(res.message || "保存成功", "success");
|
||||||
|
|||||||
@@ -63,6 +63,17 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
|
|||||||
return c.json({ message: '当前 IP 已被限制评论,请联系站长进行处理' }, 403);
|
return c.json({ message: '当前 IP 已被限制评论,请联系站长进行处理' }, 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blockedEmailsRow = await c.env.CWD_DB.prepare('SELECT value FROM Settings WHERE key = ?')
|
||||||
|
.bind('comment_blocked_emails')
|
||||||
|
.first<{ value: string }>();
|
||||||
|
const blockedEmailsValue = blockedEmailsRow?.value || '';
|
||||||
|
const blockedEmails = blockedEmailsValue
|
||||||
|
? blockedEmailsValue.split(',').map((d) => d.trim()).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
if (blockedEmails.length && blockedEmails.includes(email)) {
|
||||||
|
return c.json({ message: '当前邮箱已被限制评论,请联系站长进行处理' }, 403);
|
||||||
|
}
|
||||||
|
|
||||||
let isAdminComment = false;
|
let isAdminComment = false;
|
||||||
|
|
||||||
if (adminEmail && email === adminEmail) {
|
if (adminEmail && email === adminEmail) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const COMMENT_ALLOWED_DOMAINS_KEY = 'comment_allowed_domains';
|
|||||||
const COMMENT_ADMIN_KEY_HASH_KEY = 'comment_admin_key_hash';
|
const COMMENT_ADMIN_KEY_HASH_KEY = 'comment_admin_key_hash';
|
||||||
const COMMENT_REQUIRE_REVIEW_KEY = 'comment_require_review';
|
const COMMENT_REQUIRE_REVIEW_KEY = 'comment_require_review';
|
||||||
const COMMENT_BLOCKED_IPS_KEY = 'comment_blocked_ips';
|
const COMMENT_BLOCKED_IPS_KEY = 'comment_blocked_ips';
|
||||||
|
const COMMENT_BLOCKED_EMAILS_KEY = 'comment_blocked_emails';
|
||||||
|
|
||||||
|
|
||||||
async function loadCommentSettings(env: Bindings) {
|
async function loadCommentSettings(env: Bindings) {
|
||||||
@@ -47,10 +48,11 @@ async function loadCommentSettings(env: Bindings) {
|
|||||||
COMMENT_ALLOWED_DOMAINS_KEY,
|
COMMENT_ALLOWED_DOMAINS_KEY,
|
||||||
COMMENT_ADMIN_KEY_HASH_KEY,
|
COMMENT_ADMIN_KEY_HASH_KEY,
|
||||||
COMMENT_REQUIRE_REVIEW_KEY,
|
COMMENT_REQUIRE_REVIEW_KEY,
|
||||||
COMMENT_BLOCKED_IPS_KEY
|
COMMENT_BLOCKED_IPS_KEY,
|
||||||
|
COMMENT_BLOCKED_EMAILS_KEY
|
||||||
];
|
];
|
||||||
const { results } = await env.CWD_DB.prepare(
|
const { results } = await env.CWD_DB.prepare(
|
||||||
'SELECT key, value FROM Settings WHERE key IN (?, ?, ?, ?, ?, ?, ?, ?)'
|
'SELECT key, value FROM Settings WHERE key IN (?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||||
)
|
)
|
||||||
.bind(...keys)
|
.bind(...keys)
|
||||||
.all<{ key: string; value: string }>();
|
.all<{ key: string; value: string }>();
|
||||||
@@ -73,6 +75,11 @@ async function loadCommentSettings(env: Bindings) {
|
|||||||
? blockedIpsRaw.split(',').map((d) => d.trim()).filter(Boolean)
|
? blockedIpsRaw.split(',').map((d) => d.trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
const blockedEmailsRaw = map.get(COMMENT_BLOCKED_EMAILS_KEY) ?? '';
|
||||||
|
const blockedEmails = blockedEmailsRaw
|
||||||
|
? blockedEmailsRaw.split(',').map((d) => d.trim()).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
// 解析允许的域名列表
|
// 解析允许的域名列表
|
||||||
const allowedDomainsRaw = map.get(COMMENT_ALLOWED_DOMAINS_KEY) ?? '';
|
const allowedDomainsRaw = map.get(COMMENT_ALLOWED_DOMAINS_KEY) ?? '';
|
||||||
const allowedDomains = allowedDomainsRaw
|
const allowedDomains = allowedDomainsRaw
|
||||||
@@ -87,6 +94,7 @@ async function loadCommentSettings(env: Bindings) {
|
|||||||
allowedDomains,
|
allowedDomains,
|
||||||
requireReview,
|
requireReview,
|
||||||
blockedIps,
|
blockedIps,
|
||||||
|
blockedEmails,
|
||||||
adminKey: map.get(COMMENT_ADMIN_KEY_HASH_KEY) ?? null,
|
adminKey: map.get(COMMENT_ADMIN_KEY_HASH_KEY) ?? null,
|
||||||
adminKeySet: !!map.get(COMMENT_ADMIN_KEY_HASH_KEY)
|
adminKeySet: !!map.get(COMMENT_ADMIN_KEY_HASH_KEY)
|
||||||
};
|
};
|
||||||
@@ -103,6 +111,7 @@ async function saveCommentSettings(
|
|||||||
adminKey?: string;
|
adminKey?: string;
|
||||||
requireReview?: boolean;
|
requireReview?: boolean;
|
||||||
blockedIps?: string[];
|
blockedIps?: string[];
|
||||||
|
blockedEmails?: string[];
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
await env.CWD_DB.prepare(
|
await env.CWD_DB.prepare(
|
||||||
@@ -147,6 +156,10 @@ async function saveCommentSettings(
|
|||||||
{
|
{
|
||||||
key: COMMENT_BLOCKED_IPS_KEY,
|
key: COMMENT_BLOCKED_IPS_KEY,
|
||||||
value: settings.blockedIps ? settings.blockedIps.join(',') : undefined
|
value: settings.blockedIps ? settings.blockedIps.join(',') : undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: COMMENT_BLOCKED_EMAILS_KEY,
|
||||||
|
value: settings.blockedEmails ? settings.blockedEmails.join(',') : undefined
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -206,6 +219,7 @@ app.get('/api/config/comments', async (c) => {
|
|||||||
adminKey,
|
adminKey,
|
||||||
adminKeySet,
|
adminKeySet,
|
||||||
blockedIps,
|
blockedIps,
|
||||||
|
blockedEmails,
|
||||||
...publicSettings
|
...publicSettings
|
||||||
} = settings as any;
|
} = settings as any;
|
||||||
|
|
||||||
@@ -272,6 +286,7 @@ app.put('/admin/settings/comments', async (c) => {
|
|||||||
const rawAdminKey = typeof body.adminKey === 'string' ? body.adminKey : undefined;
|
const rawAdminKey = typeof body.adminKey === 'string' ? body.adminKey : undefined;
|
||||||
const rawRequireReview = body.requireReview;
|
const rawRequireReview = body.requireReview;
|
||||||
const rawBlockedIps = Array.isArray(body.blockedIps) ? body.blockedIps : [];
|
const rawBlockedIps = Array.isArray(body.blockedIps) ? body.blockedIps : [];
|
||||||
|
const rawBlockedEmails = Array.isArray(body.blockedEmails) ? body.blockedEmails : [];
|
||||||
|
|
||||||
const adminEmail = rawAdminEmail.trim();
|
const adminEmail = rawAdminEmail.trim();
|
||||||
const adminBadge = rawAdminBadge.trim();
|
const adminBadge = rawAdminBadge.trim();
|
||||||
@@ -291,6 +306,9 @@ app.put('/admin/settings/comments', async (c) => {
|
|||||||
const blockedIps = rawBlockedIps
|
const blockedIps = rawBlockedIps
|
||||||
.map((d: any) => (typeof d === 'string' ? d.trim() : ''))
|
.map((d: any) => (typeof d === 'string' ? d.trim() : ''))
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
const blockedEmails = rawBlockedEmails
|
||||||
|
.map((d: any) => (typeof d === 'string' ? d.trim() : ''))
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
if (adminEmail && !isValidEmail(adminEmail)) {
|
if (adminEmail && !isValidEmail(adminEmail)) {
|
||||||
return c.json({ message: '邮箱格式不正确' }, 400);
|
return c.json({ message: '邮箱格式不正确' }, 400);
|
||||||
@@ -304,7 +322,8 @@ app.put('/admin/settings/comments', async (c) => {
|
|||||||
allowedDomains,
|
allowedDomains,
|
||||||
adminKey,
|
adminKey,
|
||||||
requireReview,
|
requireReview,
|
||||||
blockedIps
|
blockedIps,
|
||||||
|
blockedEmails
|
||||||
});
|
});
|
||||||
|
|
||||||
return c.json({ message: '保存成功' });
|
return c.json({ message: '保存成功' });
|
||||||
@@ -352,4 +371,47 @@ app.post('/admin/comments/block-ip', async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/admin/comments/block-email', async (c) => {
|
||||||
|
try {
|
||||||
|
const body = await c.req.json();
|
||||||
|
const rawEmail = typeof body.email === 'string' ? body.email : '';
|
||||||
|
const email = rawEmail.trim();
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
return c.json({ message: '邮箱不能为空' }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidEmail(email)) {
|
||||||
|
return c.json({ message: '邮箱格式不正确' }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
await c.env.CWD_DB.prepare(
|
||||||
|
'CREATE TABLE IF NOT EXISTS Settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)'
|
||||||
|
).run();
|
||||||
|
|
||||||
|
const row = await c.env.CWD_DB.prepare('SELECT value FROM Settings WHERE key = ?')
|
||||||
|
.bind(COMMENT_BLOCKED_EMAILS_KEY)
|
||||||
|
.first<{ value: string }>();
|
||||||
|
|
||||||
|
const existing = row?.value || '';
|
||||||
|
const list = existing
|
||||||
|
? existing.split(',').map((d) => d.trim()).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (!list.includes(email)) {
|
||||||
|
list.push(email);
|
||||||
|
const joined = list.join(',');
|
||||||
|
await c.env.CWD_DB.prepare(
|
||||||
|
'REPLACE INTO Settings (key, value) VALUES (?, ?)'
|
||||||
|
)
|
||||||
|
.bind(COMMENT_BLOCKED_EMAILS_KEY, joined)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({ message: '已加入邮箱黑名单' });
|
||||||
|
} catch (e: any) {
|
||||||
|
return c.json({ message: e.message || '操作失败' }, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|||||||
Reference in New Issue
Block a user