feat(admin): 添加后台域名可见性管理功能
在后台设置页面新增域名管理选项卡,允许管理员配置后台下拉框中显示的域名列表。通过拖拽或点击按钮可以移动域名,实现灵活的可视域名控制。当配置了可见域名时,LayoutView 将只显示指定的域名,否则显示全部域名。
This commit is contained in:
@@ -131,6 +131,7 @@ export type FeatureSettingsResponse = {
|
|||||||
enableCommentLike: boolean;
|
enableCommentLike: boolean;
|
||||||
enableArticleLike: boolean;
|
enableArticleLike: boolean;
|
||||||
commentPlaceholder?: string;
|
commentPlaceholder?: string;
|
||||||
|
visibleDomains?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AdminDisplaySettingsResponse = {
|
export type AdminDisplaySettingsResponse = {
|
||||||
@@ -332,7 +333,7 @@ export function fetchFeatureSettings(): Promise<FeatureSettingsResponse> {
|
|||||||
return get<FeatureSettingsResponse>('/admin/settings/features');
|
return get<FeatureSettingsResponse>('/admin/settings/features');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveFeatureSettings(data: { enableCommentLike?: boolean; enableArticleLike?: boolean; commentPlaceholder?: string }): Promise<{ message: string }> {
|
export function saveFeatureSettings(data: { enableCommentLike?: boolean; enableArticleLike?: boolean; commentPlaceholder?: string; visibleDomains?: string[] }): Promise<{ message: string }> {
|
||||||
return put<{ message: string }>('/admin/settings/features', data);
|
return put<{ message: string }>('/admin/settings/features', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, watch, provide, computed } from "vue";
|
import { ref, onMounted, watch, provide, computed } from "vue";
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
import { logoutAdmin, fetchDomainList, fetchAdminDisplaySettings } from "../../api/admin";
|
import { logoutAdmin, fetchDomainList, fetchAdminDisplaySettings, fetchFeatureSettings } from "../../api/admin";
|
||||||
import { useTheme } from "../../composables/useTheme";
|
import { useTheme } from "../../composables/useTheme";
|
||||||
import packageJson from "../../../package.json";
|
import packageJson from "../../../package.json";
|
||||||
|
|
||||||
@@ -215,8 +215,18 @@ const domainOptions = ref<string[]>([]);
|
|||||||
|
|
||||||
async function loadDomains() {
|
async function loadDomains() {
|
||||||
try {
|
try {
|
||||||
const res = await fetchDomainList();
|
const [domainRes, settingsRes] = await Promise.all([
|
||||||
const domains = Array.isArray(res.domains) ? res.domains : [];
|
fetchDomainList(),
|
||||||
|
fetchFeatureSettings().catch(() => ({ visibleDomains: undefined }))
|
||||||
|
]);
|
||||||
|
|
||||||
|
let domains = Array.isArray(domainRes.domains) ? domainRes.domains : [];
|
||||||
|
|
||||||
|
// 如果配置了显示域名,则仅显示配置的域名
|
||||||
|
if (settingsRes.visibleDomains && Array.isArray(settingsRes.visibleDomains) && settingsRes.visibleDomains.length > 0) {
|
||||||
|
domains = settingsRes.visibleDomains;
|
||||||
|
}
|
||||||
|
|
||||||
const set = new Set(domains);
|
const set = new Set(domains);
|
||||||
if (domainFilter.value && !set.has(domainFilter.value)) {
|
if (domainFilter.value && !set.has(domainFilter.value)) {
|
||||||
set.add(domainFilter.value);
|
set.add(domainFilter.value);
|
||||||
|
|||||||
342
cwd-admin/src/views/SettingsView/components/DomainSettings.vue
Normal file
342
cwd-admin/src/views/SettingsView/components/DomainSettings.vue
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
<template>
|
||||||
|
<div class="domain-settings">
|
||||||
|
<div class="domain-settings-desc">
|
||||||
|
配置后台可见的域名。左侧为后台下拉框中显示的域名,右侧为数据库中发现的所有域名。支持拖拽或点击按钮移动。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="domain-transfer">
|
||||||
|
<!-- Visible Domains (Left) -->
|
||||||
|
<div class="transfer-panel">
|
||||||
|
<div class="transfer-header">
|
||||||
|
后台显示域名 ({{ visibleList.length }})
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="transfer-body"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop="onDrop($event, 'visible')"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="visibleList.length === 0"
|
||||||
|
class="transfer-empty"
|
||||||
|
>
|
||||||
|
无域名 (默认显示全部)
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="domain in visibleList"
|
||||||
|
:key="domain"
|
||||||
|
class="transfer-item"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragStart($event, domain, 'visible')"
|
||||||
|
@dblclick="moveToHidden(domain)"
|
||||||
|
>
|
||||||
|
<span class="domain-text">{{ domain }}</span>
|
||||||
|
<button class="move-btn" @click="moveToHidden(domain)" title="移出">
|
||||||
|
<PhArrowRight :size="16" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Actions -->
|
||||||
|
<div class="transfer-actions">
|
||||||
|
<button class="action-btn" @click="moveAllToVisible" title="全部左移">
|
||||||
|
<PhChecks :size="20" />
|
||||||
|
</button>
|
||||||
|
<button class="action-btn" @click="moveAllToHidden" title="全部右移">
|
||||||
|
<PhTrash :size="20" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Hidden/All Domains (Right) -->
|
||||||
|
<div class="transfer-panel">
|
||||||
|
<div class="transfer-header">
|
||||||
|
其他域名 ({{ hiddenList.length }})
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="transfer-body"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop="onDrop($event, 'hidden')"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="hiddenList.length === 0"
|
||||||
|
class="transfer-empty"
|
||||||
|
>
|
||||||
|
无更多域名
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="domain in hiddenList"
|
||||||
|
:key="domain"
|
||||||
|
class="transfer-item"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragStart($event, domain, 'hidden')"
|
||||||
|
@dblclick="moveToVisible(domain)"
|
||||||
|
>
|
||||||
|
<button class="move-btn" @click="moveToVisible(domain)" title="移入">
|
||||||
|
<PhArrowLeft :size="16" />
|
||||||
|
</button>
|
||||||
|
<span class="domain-text">{{ domain }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="btn-primary" @click="handleSave" :disabled="loading">
|
||||||
|
{{ loading ? "保存中..." : "保存" }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, computed } from 'vue';
|
||||||
|
import { fetchDomainList, fetchFeatureSettings, saveFeatureSettings } from '../../../api/admin';
|
||||||
|
import { PhArrowRight, PhArrowLeft, PhChecks, PhTrash } from '@phosphor-icons/vue';
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const allDomains = ref<string[]>([]);
|
||||||
|
const visibleList = ref<string[]>([]);
|
||||||
|
|
||||||
|
const hiddenList = computed(() => {
|
||||||
|
const visibleSet = new Set(visibleList.value);
|
||||||
|
return allDomains.value.filter(d => !visibleSet.has(d));
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadData() {
|
||||||
|
try {
|
||||||
|
const [domainRes, settingsRes] = await Promise.all([
|
||||||
|
fetchDomainList(),
|
||||||
|
fetchFeatureSettings()
|
||||||
|
]);
|
||||||
|
|
||||||
|
allDomains.value = domainRes.domains || [];
|
||||||
|
if (settingsRes.visibleDomains) {
|
||||||
|
visibleList.value = settingsRes.visibleDomains;
|
||||||
|
} else {
|
||||||
|
visibleList.value = [];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveToHidden(domain: string) {
|
||||||
|
visibleList.value = visibleList.value.filter(d => d !== domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveToVisible(domain: string) {
|
||||||
|
if (!visibleList.value.includes(domain)) {
|
||||||
|
visibleList.value.push(domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveAllToVisible() {
|
||||||
|
const current = new Set(visibleList.value);
|
||||||
|
hiddenList.value.forEach(d => current.add(d));
|
||||||
|
visibleList.value = Array.from(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveAllToHidden() {
|
||||||
|
visibleList.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragStart(event: DragEvent, domain: string, source: 'visible' | 'hidden') {
|
||||||
|
if (event.dataTransfer) {
|
||||||
|
event.dataTransfer.setData('text/plain', JSON.stringify({ domain, source }));
|
||||||
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrop(event: DragEvent, target: 'visible' | 'hidden') {
|
||||||
|
const data = event.dataTransfer?.getData('text/plain');
|
||||||
|
if (data) {
|
||||||
|
try {
|
||||||
|
const { domain, source } = JSON.parse(data);
|
||||||
|
if (source !== target) {
|
||||||
|
if (target === 'visible') {
|
||||||
|
moveToVisible(domain);
|
||||||
|
} else {
|
||||||
|
moveToHidden(domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave() {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
await saveFeatureSettings({
|
||||||
|
visibleDomains: visibleList.value
|
||||||
|
});
|
||||||
|
// 保存成功后刷新页面以应用更改(LayoutView 重新加载)
|
||||||
|
window.location.reload();
|
||||||
|
} catch (e) {
|
||||||
|
alert('保存失败');
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.domain-settings {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-settings-desc {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-transfer {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: flex-start;
|
||||||
|
height: 400px;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-panel {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--bg-card);
|
||||||
|
min-height: 300px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
font-weight: 600;
|
||||||
|
background: var(--bg-body);
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-empty {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-top: 40px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--bg-body);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
transition: background 0.2s;
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-text {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 10px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.move-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--bg-active);
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background: var(--bg-card);
|
||||||
|
color: var(--text-primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
padding: 8px 24px;
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -35,6 +35,14 @@
|
|||||||
>
|
>
|
||||||
后台显示
|
后台显示
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-tab"
|
||||||
|
:class="{ 'settings-tab-active': activeTab === 'domain' }"
|
||||||
|
@click="activeTab = 'domain'"
|
||||||
|
>
|
||||||
|
域名管理
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="settings-tab"
|
class="settings-tab"
|
||||||
@@ -280,6 +288,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="activeTab === 'domain'">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title">域名选择管理</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<DomainSettings />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template v-else-if="activeTab === 'emailNotify'">
|
<template v-else-if="activeTab === 'emailNotify'">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@@ -527,6 +545,8 @@ import {
|
|||||||
sendTelegramTestMessage,
|
sendTelegramTestMessage,
|
||||||
} from "../../api/admin";
|
} from "../../api/admin";
|
||||||
|
|
||||||
|
import DomainSettings from "./components/DomainSettings.vue";
|
||||||
|
|
||||||
const DEFAULT_REPLY_TEMPLATE = `<div style="background-color:#f4f4f5;padding:24px 0;">
|
const DEFAULT_REPLY_TEMPLATE = `<div style="background-color:#f4f4f5;padding:24px 0;">
|
||||||
<div style="max-width:640px;margin:0 auto;background:#ffffff;border-radius:12px;border:1px solid #e5e7eb;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;color:#111827;">
|
<div style="max-width:640px;margin:0 auto;background:#ffffff;border-radius:12px;border:1px solid #e5e7eb;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;color:#111827;">
|
||||||
<div style="padding:20px 28px;border-bottom:1px solid #e5e7eb;background:linear-gradient(135deg,#2563eb,#4f46e5);">
|
<div style="padding:20px 28px;border-bottom:1px solid #e5e7eb;background:linear-gradient(135deg,#2563eb,#4f46e5);">
|
||||||
@@ -627,7 +647,7 @@ const savingTelegram = ref(false);
|
|||||||
const settingUpWebhook = ref(false);
|
const settingUpWebhook = ref(false);
|
||||||
const testingTelegram = ref(false);
|
const testingTelegram = ref(false);
|
||||||
|
|
||||||
const activeTab = ref<"comment" | "feature" | "display" | "emailNotify" | "telegramNotify">(
|
const activeTab = ref<"comment" | "feature" | "display" | "emailNotify" | "telegramNotify" | "domain">(
|
||||||
"comment",
|
"comment",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,15 @@ export const updateFeatureSettings = async (c: Context<{ Bindings: Bindings }>)
|
|||||||
const commentPlaceholder =
|
const commentPlaceholder =
|
||||||
rawCommentPlaceholder !== undefined ? rawCommentPlaceholder.trim() : undefined;
|
rawCommentPlaceholder !== undefined ? rawCommentPlaceholder.trim() : undefined;
|
||||||
|
|
||||||
|
const visibleDomains = Array.isArray(body.visibleDomains)
|
||||||
|
? (body.visibleDomains as string[])
|
||||||
|
: undefined;
|
||||||
|
|
||||||
await saveFeatureSettings(c.env, {
|
await saveFeatureSettings(c.env, {
|
||||||
enableCommentLike,
|
enableCommentLike,
|
||||||
enableArticleLike,
|
enableArticleLike,
|
||||||
commentPlaceholder
|
commentPlaceholder,
|
||||||
|
visibleDomains
|
||||||
});
|
});
|
||||||
|
|
||||||
return c.json({ message: '保存成功!' });
|
return c.json({ message: '保存成功!' });
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import { Bindings } from '../bindings';
|
|||||||
export const FEATURE_COMMENT_LIKE_KEY = 'comment_feature_comment_like';
|
export const FEATURE_COMMENT_LIKE_KEY = 'comment_feature_comment_like';
|
||||||
export const FEATURE_ARTICLE_LIKE_KEY = 'comment_feature_article_like';
|
export const FEATURE_ARTICLE_LIKE_KEY = 'comment_feature_article_like';
|
||||||
export const FEATURE_COMMENT_PLACEHOLDER_KEY = 'comment_feature_placeholder';
|
export const FEATURE_COMMENT_PLACEHOLDER_KEY = 'comment_feature_placeholder';
|
||||||
|
export const FEATURE_VISIBLE_DOMAINS_KEY = 'admin_visible_domains';
|
||||||
|
|
||||||
export type FeatureSettings = {
|
export type FeatureSettings = {
|
||||||
enableCommentLike: boolean;
|
enableCommentLike: boolean;
|
||||||
enableArticleLike: boolean;
|
enableArticleLike: boolean;
|
||||||
commentPlaceholder?: string;
|
commentPlaceholder?: string;
|
||||||
|
visibleDomains?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function loadFeatureSettings(env: Bindings): Promise<FeatureSettings> {
|
export async function loadFeatureSettings(env: Bindings): Promise<FeatureSettings> {
|
||||||
@@ -15,9 +17,14 @@ export async function loadFeatureSettings(env: Bindings): Promise<FeatureSetting
|
|||||||
'CREATE TABLE IF NOT EXISTS Settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)'
|
'CREATE TABLE IF NOT EXISTS Settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)'
|
||||||
).run();
|
).run();
|
||||||
|
|
||||||
const keys = [FEATURE_COMMENT_LIKE_KEY, FEATURE_ARTICLE_LIKE_KEY, FEATURE_COMMENT_PLACEHOLDER_KEY];
|
const keys = [
|
||||||
|
FEATURE_COMMENT_LIKE_KEY,
|
||||||
|
FEATURE_ARTICLE_LIKE_KEY,
|
||||||
|
FEATURE_COMMENT_PLACEHOLDER_KEY,
|
||||||
|
FEATURE_VISIBLE_DOMAINS_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 }>();
|
||||||
@@ -51,10 +58,21 @@ export async function loadFeatureSettings(env: Bindings): Promise<FeatureSetting
|
|||||||
|
|
||||||
const commentPlaceholder = map.get(FEATURE_COMMENT_PLACEHOLDER_KEY);
|
const commentPlaceholder = map.get(FEATURE_COMMENT_PLACEHOLDER_KEY);
|
||||||
|
|
||||||
|
let visibleDomains: string[] | undefined;
|
||||||
|
const visibleDomainsRaw = map.get(FEATURE_VISIBLE_DOMAINS_KEY);
|
||||||
|
if (visibleDomainsRaw) {
|
||||||
|
try {
|
||||||
|
visibleDomains = JSON.parse(visibleDomainsRaw);
|
||||||
|
} catch (e) {
|
||||||
|
// ignore error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enableCommentLike,
|
enableCommentLike,
|
||||||
enableArticleLike,
|
enableArticleLike,
|
||||||
commentPlaceholder
|
commentPlaceholder,
|
||||||
|
visibleDomains
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +106,10 @@ export async function saveFeatureSettings(
|
|||||||
{
|
{
|
||||||
key: FEATURE_COMMENT_PLACEHOLDER_KEY,
|
key: FEATURE_COMMENT_PLACEHOLDER_KEY,
|
||||||
value: settings.commentPlaceholder
|
value: settings.commentPlaceholder
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: FEATURE_VISIBLE_DOMAINS_KEY,
|
||||||
|
value: settings.visibleDomains ? JSON.stringify(settings.visibleDomains) : undefined
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user