You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

215 lines
6.9 KiB

<script setup lang="ts">
import { useAuthSession } from '../../../../composables/useAuthSession'
usePageTitle('应用配置')
type GlobalConfigPayload = {
config: {
siteName: string
allowRegister: boolean
mediaOrphanAutoSweepEnabled: boolean
mediaOrphanAutoSweepIntervalMinutes: number
commentEmailNotifyEnabled: boolean
commentMailFromEmail: string
commentSmtpHost: string
commentSmtpPort: number
commentSmtpSecure: boolean
commentSmtpUser: string
commentSmtpPass: string
}
}
const { user, refresh } = useAuthSession()
const { fetchData } = useClientApi()
const toast = useToast()
const { refresh: refreshGlobalConfig } = useGlobalConfig()
const loading = ref(true)
const saving = ref(false)
const siteName = ref('')
const allowRegister = ref(true)
const mediaOrphanAutoSweepEnabled = ref(false)
const mediaOrphanAutoSweepIntervalMinutes = ref(60)
const commentEmailNotifyEnabled = ref(false)
const commentMailFromEmail = ref('')
const commentSmtpHost = ref('')
const commentSmtpPort = ref(465)
const commentSmtpSecure = ref(true)
const commentSmtpUser = ref('')
const commentSmtpPass = ref('')
async function ensureAdmin() {
await refresh(true)
if (user.value?.role !== 'admin') {
await navigateTo('/me')
}
}
async function load() {
loading.value = true
try {
const { config: cfg } = await fetchData<GlobalConfigPayload>('/api/config/global')
siteName.value = cfg.siteName
allowRegister.value = cfg.allowRegister
mediaOrphanAutoSweepEnabled.value = cfg.mediaOrphanAutoSweepEnabled
mediaOrphanAutoSweepIntervalMinutes.value = cfg.mediaOrphanAutoSweepIntervalMinutes
commentEmailNotifyEnabled.value = cfg.commentEmailNotifyEnabled
commentMailFromEmail.value = cfg.commentMailFromEmail
commentSmtpHost.value = cfg.commentSmtpHost
commentSmtpPort.value = cfg.commentSmtpPort
commentSmtpSecure.value = cfg.commentSmtpSecure
commentSmtpUser.value = cfg.commentSmtpUser
commentSmtpPass.value = cfg.commentSmtpPass
} finally {
loading.value = false
}
}
onMounted(async () => {
await ensureAdmin()
await load()
})
async function putKey(key: string, value: unknown) {
await fetchData('/api/config/global', {
method: 'PUT',
body: { key, value },
})
}
async function save() {
saving.value = true
try {
await putKey('siteName', siteName.value.trim())
await putKey('allowRegister', allowRegister.value)
await putKey('mediaOrphanAutoSweepEnabled', mediaOrphanAutoSweepEnabled.value)
await putKey('mediaOrphanAutoSweepIntervalMinutes', mediaOrphanAutoSweepIntervalMinutes.value)
await putKey('commentEmailNotifyEnabled', commentEmailNotifyEnabled.value)
await putKey('commentMailFromEmail', commentMailFromEmail.value.trim())
await putKey('commentSmtpHost', commentSmtpHost.value.trim())
await putKey('commentSmtpPort', commentSmtpPort.value)
await putKey('commentSmtpSecure', commentSmtpSecure.value)
await putKey('commentSmtpUser', commentSmtpUser.value.trim())
await putKey('commentSmtpPass', commentSmtpPass.value.trim())
await load()
await refreshGlobalConfig()
toast.add({ title: '配置已保存', color: 'success' })
} finally {
saving.value = false
}
}
</script>
<template>
<UContainer class="py-8 space-y-8 max-w-4xl">
<h1 class="text-2xl font-semibold">
应用配置
</h1>
<p class="text-sm text-muted">
全局设置站点名称开放注册媒体孤儿自动清扫与评论通知邮件
</p>
<UCard>
<template #header>
全局项
</template>
<div v-if="loading" class="text-muted">
加载中…
</div>
<div v-else class="space-y-4 max-w-xl">
<UFormField label="站点名称">
<UInput v-model="siteName" maxlength="64" placeholder="Person Panel" />
</UFormField>
<UFormField
label="允许自助注册"
description="关闭后,登录页不再显示注册入口;管理员仍可创建用户。"
>
<UCheckbox v-model="allowRegister" label="开启" />
</UFormField>
<UFormField
label="媒体孤儿自动清扫"
description="全站生效、仅删除已过宽限期且无引用的图片、建议先在「图片孤儿审查」确认。"
>
<UCheckbox v-model="mediaOrphanAutoSweepEnabled" label="开启定时自动清扫" />
</UFormField>
<UFormField
label="清扫间隔(分钟)"
description="15–1440;与定时任务触发频率配合,任务内会按此间隔节流。"
>
<UInput
v-model.number="mediaOrphanAutoSweepIntervalMinutes"
type="number"
:min="15"
:max="1440"
step="1"
/>
</UFormField>
<UButton :loading="saving" @click="save">
保存
</UButton>
</div>
</UCard>
<UCard>
<template #header>
评论通知邮件
</template>
<div v-if="loading" class="text-muted">
加载中…
</div>
<div v-else class="space-y-4 max-w-xl">
<UFormField
label="启用评论邮件通知"
description="关闭后将跳过评论邮件发送。"
>
<UCheckbox v-model="commentEmailNotifyEnabled" label="开启通知发送" />
</UFormField>
<UFormField
label="发件人邮箱"
description="用于邮件 From 字段,留空表示未配置。"
>
<UInput v-model="commentMailFromEmail" placeholder="noreply@example.com" />
</UFormField>
<UFormField
label="SMTP 主机"
description="例如 smtp.example.com。"
>
<UInput v-model="commentSmtpHost" placeholder="smtp.example.com" />
</UFormField>
<UFormField
label="SMTP 端口"
description="范围 1-65535,常见 465 或 587。"
>
<UInput
v-model.number="commentSmtpPort"
type="number"
:min="1"
:max="65535"
step="1"
/>
</UFormField>
<UFormField
label="SMTP 安全连接"
description="通常 SSL 对应 465,TLS/STARTTLS 可按服务商要求配置。"
>
<UCheckbox v-model="commentSmtpSecure" label="使用安全连接" />
</UFormField>
<UFormField
label="SMTP 用户名"
description="通常为邮箱地址或服务商提供的账号。"
>
<UInput v-model="commentSmtpUser" placeholder="smtp user" />
</UFormField>
<UFormField
label="SMTP 密码"
description="建议使用邮箱服务商的应用专用密码。"
>
<UInput v-model="commentSmtpPass" type="password" placeholder="smtp password" />
</UFormField>
<UButton :loading="saving" @click="save">
保存
</UButton>
</div>
</UCard>
</UContainer>
</template>