feat(admin): 添加域名白名单功能并支持Artalk导入
- 在评论设置中添加域名白名单配置,限制组件调用 - 支持从Artalk系统导入评论数据 - 完善Twikoo和Artalk数据结构的映射处理 - 在组件端实现域名白名单检查逻辑
This commit is contained in:
@@ -39,6 +39,7 @@ export type CommentSettingsResponse = {
|
||||
adminBadge: string | null;
|
||||
avatarPrefix: string | null;
|
||||
adminEnabled: boolean;
|
||||
allowedDomains?: string[];
|
||||
};
|
||||
|
||||
export type EmailNotifySettingsResponse = {
|
||||
@@ -122,6 +123,7 @@ export function saveCommentSettings(data: {
|
||||
adminBadge?: string;
|
||||
avatarPrefix?: string;
|
||||
adminEnabled?: boolean;
|
||||
allowedDomains?: string[];
|
||||
}): Promise<{ message: string }> {
|
||||
return put<{ message: string }>('/admin/settings/comments', data);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<div class="form-group">
|
||||
<label class="form-label">来源系统</label>
|
||||
<select v-model="importSource" class="form-select">
|
||||
<option value="twikoo">Twikoo</option>
|
||||
<option value="twikoo">Twikoo (.json)</option>
|
||||
<option value="artalk">Artalk (.json)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -188,8 +189,8 @@ async function handleFileChange(event: Event) {
|
||||
// 检测前缀逻辑
|
||||
let missingCount = 0;
|
||||
for (const item of comments) {
|
||||
// 优先检查 Twikoo 字段 href,其次检查 CWD 字段 post_slug
|
||||
const url = item.href || item.post_slug;
|
||||
// 优先检查 Twikoo 字段 href,其次 Artalk 字段 page_key,最后 CWD 字段 post_slug
|
||||
const url = item.href || item.page_key || item.post_slug;
|
||||
if (url && typeof url === "string") {
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
missingCount++;
|
||||
@@ -250,6 +251,16 @@ async function confirmPrefix() {
|
||||
}
|
||||
}
|
||||
|
||||
// Artalk
|
||||
if (newItem.page_key && typeof newItem.page_key === "string") {
|
||||
if (
|
||||
!newItem.page_key.startsWith("http://") &&
|
||||
!newItem.page_key.startsWith("https://")
|
||||
) {
|
||||
newItem.page_key = joinUrl(prefix, newItem.page_key);
|
||||
}
|
||||
}
|
||||
|
||||
// CWD
|
||||
if (newItem.post_slug && typeof newItem.post_slug === "string") {
|
||||
if (
|
||||
|
||||
@@ -31,6 +31,15 @@
|
||||
<label class="form-label">头像前缀(默认:https://gravatar.com/avatar)</label>
|
||||
<input v-model="avatarPrefix" class="form-input" type="text" />
|
||||
</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="card-actions">
|
||||
<button class="card-button" :disabled="savingComment" @click="saveComment">
|
||||
<span v-if="savingComment">保存中...</span>
|
||||
@@ -134,6 +143,7 @@ const commentAdminEmail = ref("");
|
||||
const commentAdminBadge = ref("");
|
||||
const avatarPrefix = ref("");
|
||||
const commentAdminEnabled = ref(false);
|
||||
const allowedDomains = ref("");
|
||||
const savingEmail = ref(false);
|
||||
const testingEmail = ref(false);
|
||||
const savingComment = ref(false);
|
||||
@@ -181,6 +191,7 @@ async function load() {
|
||||
commentAdminBadge.value = commentRes.adminBadge || "博主";
|
||||
avatarPrefix.value = commentRes.avatarPrefix || "";
|
||||
commentAdminEnabled.value = !!commentRes.adminEnabled;
|
||||
allowedDomains.value = commentRes.allowedDomains ? commentRes.allowedDomains.join(", ") : "";
|
||||
emailGlobalEnabled.value = !!emailNotifyRes.globalEnabled;
|
||||
|
||||
if (emailNotifyRes.smtp) {
|
||||
@@ -287,6 +298,7 @@ async function saveComment() {
|
||||
adminBadge: commentAdminBadge.value,
|
||||
avatarPrefix: avatarPrefix.value,
|
||||
adminEnabled: commentAdminEnabled.value,
|
||||
allowedDomains: allowedDomains.value.split(/[,,\n]/).map(d => d.trim()).filter(Boolean)
|
||||
});
|
||||
showToast(res.message || "保存成功", "success");
|
||||
} catch (e: any) {
|
||||
|
||||
Reference in New Issue
Block a user