feat(comments): 添加邮箱黑名单功能
实现评论系统的邮箱黑名单功能,包括: 1. 在API中添加邮箱黑名单检查逻辑 2. 在管理后台添加邮箱黑名单设置界面 3. 支持在评论列表中直接屏蔽邮箱 4. 添加相关API接口处理邮箱黑名单操作
This commit is contained in:
@@ -44,6 +44,7 @@ export type CommentSettingsResponse = {
|
||||
adminKeySet?: boolean;
|
||||
requireReview?: boolean;
|
||||
blockedIps?: string[];
|
||||
blockedEmails?: string[];
|
||||
};
|
||||
|
||||
export type EmailNotifySettingsResponse = {
|
||||
@@ -139,6 +140,7 @@ export function saveCommentSettings(data: {
|
||||
adminKey?: string;
|
||||
requireReview?: boolean;
|
||||
blockedIps?: string[];
|
||||
blockedEmails?: string[];
|
||||
}): Promise<{ message: string }> {
|
||||
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 });
|
||||
}
|
||||
|
||||
export function blockEmail(email: string): Promise<{ message: string }> {
|
||||
return post<{ message: string }>('/admin/comments/block-email', { email });
|
||||
}
|
||||
|
||||
export function exportComments(): Promise<any[]> {
|
||||
return get<any[]>('/admin/comments/export');
|
||||
}
|
||||
|
||||
@@ -36,7 +36,15 @@
|
||||
/>
|
||||
<div class="cell-author-main">
|
||||
<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>
|
||||
<div v-if="item.ipAddress" class="cell-author-ip">
|
||||
<span class="cell-ip-text" @click="handleBlockIp(item)" title="屏蔽该 IP">{{ item.ipAddress }}</span>
|
||||
@@ -180,6 +188,7 @@ import {
|
||||
deleteComment,
|
||||
updateCommentStatus,
|
||||
blockIp,
|
||||
blockEmail,
|
||||
} from "../api/admin";
|
||||
|
||||
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(() => {
|
||||
loadComments();
|
||||
});
|
||||
@@ -479,6 +503,14 @@ onMounted(() => {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.cell-email-text {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cell-email-text:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cell-content-text {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
|
||||
@@ -39,27 +39,9 @@
|
||||
<input v-model="avatarPrefix" class="form-input" type="text" />
|
||||
</div>
|
||||
<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">
|
||||
<label class="form-label">管理员评论密钥</label>
|
||||
<div class="form-hint" style="margin-bottom: 4px;">
|
||||
<div class="form-hint" style="margin-bottom: 4px">
|
||||
设置后前台使用管理员邮箱评论需输入此密钥。
|
||||
</div>
|
||||
<input
|
||||
@@ -69,6 +51,40 @@
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</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">
|
||||
<button class="card-button" :disabled="savingComment" @click="saveComment">
|
||||
<span v-if="savingComment">保存中...</span>
|
||||
@@ -205,7 +221,7 @@
|
||||
</button>
|
||||
<button
|
||||
class="card-button secondary"
|
||||
style="margin-left: auto;"
|
||||
style="margin-left: auto"
|
||||
@click="resetTemplatesToDefault"
|
||||
>
|
||||
恢复默认模板
|
||||
@@ -315,6 +331,7 @@ const avatarPrefix = ref("");
|
||||
const commentAdminEnabled = ref(false);
|
||||
const allowedDomains = ref("");
|
||||
const blockedIps = ref("");
|
||||
const blockedEmails = ref("");
|
||||
const commentAdminKey = ref("");
|
||||
const adminKeySet = ref(false);
|
||||
const requireReview = ref(false);
|
||||
@@ -375,8 +392,9 @@ async function load() {
|
||||
allowedDomains.value = commentRes.allowedDomains
|
||||
? commentRes.allowedDomains.join(", ")
|
||||
: "";
|
||||
blockedIps.value = commentRes.blockedIps
|
||||
? commentRes.blockedIps.join(", ")
|
||||
blockedIps.value = commentRes.blockedIps ? commentRes.blockedIps.join(", ") : "";
|
||||
blockedEmails.value = commentRes.blockedEmails
|
||||
? commentRes.blockedEmails.join(", ")
|
||||
: "";
|
||||
commentAdminKey.value = commentRes.adminKey || "";
|
||||
adminKeySet.value = !!commentRes.adminKeySet;
|
||||
@@ -384,10 +402,8 @@ async function load() {
|
||||
emailGlobalEnabled.value = !!emailNotifyRes.globalEnabled;
|
||||
|
||||
if (emailNotifyRes.templates) {
|
||||
templateAdmin.value =
|
||||
emailNotifyRes.templates.admin || DEFAULT_ADMIN_TEMPLATE;
|
||||
templateReply.value =
|
||||
emailNotifyRes.templates.reply || DEFAULT_REPLY_TEMPLATE;
|
||||
templateAdmin.value = emailNotifyRes.templates.admin || DEFAULT_ADMIN_TEMPLATE;
|
||||
templateReply.value = emailNotifyRes.templates.reply || DEFAULT_REPLY_TEMPLATE;
|
||||
} else {
|
||||
templateAdmin.value = DEFAULT_ADMIN_TEMPLATE;
|
||||
templateReply.value = DEFAULT_REPLY_TEMPLATE;
|
||||
@@ -515,6 +531,10 @@ async function saveComment() {
|
||||
.split(/[,,\n]/)
|
||||
.map((d) => d.trim())
|
||||
.filter(Boolean),
|
||||
blockedEmails: blockedEmails.value
|
||||
.split(/[,,\n]/)
|
||||
.map((d) => d.trim())
|
||||
.filter(Boolean),
|
||||
});
|
||||
|
||||
showToast(res.message || "保存成功", "success");
|
||||
|
||||
Reference in New Issue
Block a user