updat: config

This commit is contained in:
Kerwin
2025-11-20 19:27:39 +08:00
parent 9e6b5a58c4
commit 99d2c7f20f
5 changed files with 491 additions and 34 deletions

View File

@@ -353,10 +353,16 @@ func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]inte
response["telegram_qr_image"] = config.Value
case entity.ConfigKeyQrCodeStyle:
response["qr_code_style"] = config.Value
// 跳过不需要返回给公众的配置
case entity.ConfigKeyAutoProcessReadyResources:
case entity.ConfigKeyAutoProcessInterval:
if val, err := strconv.ParseBool(config.Value); err == nil {
response["auto_process_ready_resources"] = val
}
case entity.ConfigKeyAutoTransferEnabled:
if val, err := strconv.ParseBool(config.Value); err == nil {
response["auto_transfer_enabled"] = val
}
// 跳过不需要返回给公众的配置
case entity.ConfigKeyAutoProcessInterval:
case entity.ConfigKeyAutoTransferLimitDays:
case entity.ConfigKeyAutoTransferMinSpace:
case entity.ConfigKeyAutoFetchHotDramaEnabled:

View File

@@ -0,0 +1,204 @@
<template>
<div v-if="showCacheInfo && isClient" class="fixed bottom-4 right-4 z-50 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 p-4 max-w-sm">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100">系统配置缓存状态</h3>
<button
@click="showCacheInfo = false"
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
>
<i class="fas fa-times"></i>
</button>
</div>
<div class="space-y-2 text-xs">
<!-- 初始化状态 -->
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">初始化状态:</span>
<span :class="status.initialized ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'">
{{ status.initialized ? '已初始化' : '未初始化' }}
</span>
</div>
<!-- 加载状态 -->
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">加载状态:</span>
<span :class="status.isLoading ? 'text-blue-600 dark:text-blue-400' : 'text-gray-600 dark:text-gray-400'">
{{ status.isLoading ? '加载中...' : '空闲' }}
</span>
</div>
<!-- 缓存状态 -->
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">缓存状态:</span>
<span :class="status.isCacheValid ? 'text-green-600 dark:text-green-400' : 'text-orange-600 dark:text-orange-400'">
{{ status.isCacheValid ? '有效' : '无效/过期' }}
</span>
</div>
<!-- 缓存剩余时间 -->
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">缓存剩余:</span>
<span class="text-gray-900 dark:text-gray-100">
{{ formatTime(status.cacheTimeRemaining) }}
</span>
</div>
<!-- 最后获取时间 -->
<div class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">最后更新:</span>
<span class="text-gray-900 dark:text-gray-100">
{{ formatLastFetch(status.lastFetchTime) }}
</span>
</div>
<!-- 错误信息 -->
<div v-if="status.error" class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">错误:</span>
<span class="text-red-600 dark:text-red-400">
{{ status.error }}
</span>
</div>
</div>
<!-- 操作按钮 -->
<div class="mt-4 flex gap-2">
<button
@click="refreshCache"
:disabled="status.isLoading"
class="flex-1 px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
<i class="fas fa-sync-alt mr-1"></i>
刷新
</button>
<button
@click="clearCache"
class="flex-1 px-2 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600"
>
<i class="fas fa-trash mr-1"></i>
清除
</button>
</div>
</div>
<!-- 浮动按钮仅在开发环境和客户端显示 -->
<button
v-if="isDev && isClient"
@click="showCacheInfo = !showCacheInfo"
class="fixed bottom-4 right-4 z-40 w-12 h-12 bg-purple-500 text-white rounded-full shadow-lg hover:bg-purple-600 transition-colors flex items-center justify-center"
title="系统配置缓存信息"
>
<i class="fas fa-database"></i>
</button>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useSystemConfigStore } from '~/stores/systemConfig'
const systemConfigStore = useSystemConfigStore()
const showCacheInfo = ref(false)
// 检查是否为开发环境
const isDev = computed(() => {
return process.env.NODE_ENV === 'development'
})
// 检查是否为客户端
const isClient = computed(() => {
return process.client
})
// 获取状态信息 - 直接访问store的响应式状态以确保正确更新
const status = computed(() => ({
initialized: systemConfigStore.initialized,
isLoading: systemConfigStore.isLoading,
error: systemConfigStore.error,
lastFetchTime: systemConfigStore.lastFetchTime,
cacheTimeRemaining: systemConfigStore.cacheTimeRemaining,
isCacheValid: systemConfigStore.isCacheValid
}))
// 格式化时间显示
const formatTime = (seconds: number): string => {
if (seconds <= 0) return '已过期'
const minutes = Math.floor(seconds / 60)
const remainingSeconds = seconds % 60
if (minutes > 0) {
return `${minutes}${remainingSeconds}`
} else {
return `${remainingSeconds}`
}
}
// 格式化最后获取时间
const formatLastFetch = (timestamp: number): string => {
if (!timestamp) return '从未'
const now = Date.now()
const diff = now - timestamp
if (diff < 60 * 1000) {
return '刚刚'
} else if (diff < 60 * 60 * 1000) {
const minutes = Math.floor(diff / (60 * 1000))
return `${minutes}分钟前`
} else if (diff < 24 * 60 * 60 * 1000) {
const hours = Math.floor(diff / (60 * 60 * 1000))
return `${hours}小时前`
} else {
const date = new Date(timestamp)
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
}
// 刷新缓存
const refreshCache = async () => {
try {
await systemConfigStore.refreshConfig()
console.log('[CacheInfo] 手动刷新缓存完成')
} catch (error) {
console.error('[CacheInfo] 刷新缓存失败:', error)
}
}
// 清除缓存
const clearCache = () => {
systemConfigStore.clearCache()
console.log('[CacheInfo] 手动清除缓存完成')
}
// 键盘快捷键支持
const handleKeydown = (e: KeyboardEvent) => {
// Ctrl+Shift+C 显示/隐藏缓存信息(仅在开发环境)
if (isDev.value && e.ctrlKey && e.shiftKey && e.key === 'C') {
e.preventDefault()
showCacheInfo.value = !showCacheInfo.value
}
}
onMounted(() => {
if (isClient.value) {
document.addEventListener('keydown', handleKeydown)
}
})
onUnmounted(() => {
if (isClient.value) {
document.removeEventListener('keydown', handleKeydown)
}
})
</script>
<style scoped>
/* 添加一些动画效果 */
.transition-colors {
transition: color 0.2s, background-color 0.2s;
}
</style>

View File

@@ -301,25 +301,35 @@
</div>
</div>
</div>
<!-- 开发环境缓存信息组件 -->
<SystemConfigCacheInfo />
</template>
<script setup lang="ts">
// 获取运行时配置
const config = useRuntimeConfig()
import { useResourceApi, useStatsApi, usePanApi, useSystemConfigApi, usePublicSystemConfigApi, useSearchStatsApi } from '~/composables/useApi'
import { useResourceApi, useStatsApi, usePanApi, useSearchStatsApi } from '~/composables/useApi'
import SystemConfigCacheInfo from '~/components/SystemConfigCacheInfo.vue'
const resourceApi = useResourceApi()
const statsApi = useStatsApi()
const panApi = usePanApi()
const publicSystemConfigApi = usePublicSystemConfigApi()
// 路由参数已通过自动导入提供,直接使用
const route = useRoute()
const router = useRouter()
// 页面元数据 - 使用系统配置的标题
const { data: systemConfigData } = await useAsyncData('systemConfig', () => publicSystemConfigApi.getPublicSystemConfig())
// 使用系统配置Store带缓存支持
const { useSystemConfigStore } = await import('~/stores/systemConfig')
const systemConfigStore = useSystemConfigStore()
// 初始化系统配置(会自动使用缓存)
await systemConfigStore.initConfig()
// 检查并自动刷新即将过期的缓存
await systemConfigStore.checkAndRefreshCache()
// 获取平台名称的辅助函数
const getPlatformName = (platformId: string) => {
@@ -329,12 +339,11 @@ const getPlatformName = (platformId: string) => {
return platform?.name || ''
}
// 动态生成页面标题和meta信息 - 修复安全访问问题
// 动态生成页面标题和meta信息 - 使用缓存的系统配置
const pageTitle = computed(() => {
try {
const config = systemConfigData.value as any
const siteTitle = (config?.data?.site_title) ? config.data.site_title :
(config?.site_title) ? config.site_title : '老九网盘资源数据库'
const config = systemConfigStore.config
const siteTitle = config?.site_title || '老九网盘资源数据库'
const searchKeyword = (route.query?.search) ? route.query.search as string : ''
const platformId = (route.query?.platform) ? route.query.platform as string : ''
const platformName = getPlatformName(platformId)
@@ -360,9 +369,8 @@ const pageTitle = computed(() => {
const pageDescription = computed(() => {
try {
const config = systemConfigData.value as any
const baseDescription = (config?.data?.site_description) ? config.data.site_description :
(config?.site_description) ? config.site_description : '老九网盘资源管理系统, 一个现代化的网盘资源数据库,支持多网盘自动化转存分享,支持百度网盘,阿里云盘,夸克网盘, 天翼云盘迅雷云盘123云盘115网盘UC网盘'
const config = systemConfigStore.config
const baseDescription = config?.site_description || '老九网盘资源管理系统, 一个现代化的网盘资源数据库,支持多网盘自动化转存分享,支持百度网盘,阿里云盘,夸克网盘, 天翼云盘迅雷云盘123云盘115网盘UC网盘'
const searchKeyword = (route.query && route.query.search) ? route.query.search as string : ''
const platformId = (route.query && route.query.platform) ? route.query.platform as string : ''
@@ -388,9 +396,8 @@ const pageDescription = computed(() => {
const pageKeywords = computed(() => {
try {
const config = systemConfigData.value as any
const baseKeywords = (config?.data?.keywords) ? config.data.keywords :
(config?.keywords) ? config.keywords : '网盘资源,资源管理,数据库'
const config = systemConfigStore.config
const baseKeywords = config?.keywords || '网盘资源,资源管理,数据库'
const searchKeyword = (route.query && route.query.search) ? route.query.search as string : ''
const platformId = (route.query && route.query.platform) ? route.query.platform as string : ''
@@ -474,7 +481,7 @@ onBeforeMount(async () => {
// 监听路由变化和系统配置数据当搜索条件或配置改变时更新SEO
watch(
() => [route.query?.search, route.query?.platform, systemConfigData.value],
() => [route.query?.search, route.query?.platform, systemConfigStore.config],
() => {
// 使用nextTick确保响应式数据已更新
nextTick(() => {
@@ -626,7 +633,7 @@ const safeResources = computed(() => {
})
const safeStats = computed(() => (statsData.value as any) || { total_resources: 0, total_categories: 0, total_tags: 0, total_views: 0, today_resources: 0 })
const platforms = computed(() => (platformsData.value as any) || [])
const systemConfig = computed(() => (systemConfigData.value as any)?.data || { site_title: '老九网盘资源数据库' })
const systemConfig = computed(() => systemConfigStore.config || { site_title: '老九网盘资源数据库' })
const safeLoading = computed(() => pending.value)

View File

@@ -483,6 +483,9 @@
</div>
</div>
</div>
<!-- 开发环境缓存信息组件 -->
<SystemConfigCacheInfo />
</template>
<script setup lang="ts">
@@ -495,6 +498,7 @@ import { useNotification } from 'naive-ui'
// 导入API
import { useResourceApi } from '~/composables/useApi'
import SystemConfigCacheInfo from '~/components/SystemConfigCacheInfo.vue'
// 导入组件
import SearchButton from '~/components/SearchButton.vue'
@@ -527,8 +531,17 @@ const hotResources = ref<any[]>([])
const hotResourcesLoading = ref(true)
// 获取系统配置
const { data: systemConfigData } = await useAsyncData('systemConfig', () => publicSystemConfigApi.getPublicSystemConfig())
const systemConfig = computed(() => (systemConfigData.value as any)?.data || { site_title: '老九网盘资源数据库' })
// 使用系统配置Store带缓存支持
const { useSystemConfigStore } = await import('~/stores/systemConfig')
const systemConfigStore = useSystemConfigStore()
// 初始化系统配置(会自动使用缓存)
await systemConfigStore.initConfig()
// 检查并自动刷新即将过期的缓存
await systemConfigStore.checkAndRefreshCache()
const systemConfig = computed(() => systemConfigStore.config || { site_title: '老九网盘资源数据库' })
// 获取资源数据
const { data: resourcesData, error: resourcesError } = await useAsyncData(

View File

@@ -2,38 +2,265 @@ import { defineStore } from 'pinia'
import { useApiFetch } from '~/composables/useApiFetch'
import { parseApiResponse } from '~/composables/useApi'
// 缓存配置
const CACHE_KEY = 'system-config-cache'
const CACHE_TIMESTAMP_KEY = 'system-config-cache-timestamp'
const CACHE_DURATION = 30 * 60 * 1000 // 30分钟缓存
// 安全的客户端检查函数
const isClient = () => {
return typeof process !== 'undefined' && process.client
}
interface CacheData {
config: any
timestamp: number
version?: string
}
export const useSystemConfigStore = defineStore('systemConfig', {
state: () => ({
config: null as any,
initialized: false
initialized: false,
lastFetchTime: 0 as number,
isLoading: false as boolean,
error: null as string | null
}),
actions: {
async initConfig(force = false, useAdminApi = false) {
if (this.initialized && !force) return
getters: {
// 检查缓存是否有效
isCacheValid(): boolean {
if (!isClient()) return false
try {
const cacheData = localStorage.getItem(CACHE_KEY)
const cacheTimestamp = localStorage.getItem(CACHE_TIMESTAMP_KEY)
if (!cacheData || !cacheTimestamp) return false
const timestamp = parseInt(cacheTimestamp)
const now = Date.now()
// 检查缓存是否过期
const isValid = (now - timestamp) < CACHE_DURATION
console.log(`[SystemConfig] 缓存检查: ${isValid ? '有效' : '已过期'}, 剩余时间: ${Math.max(0, CACHE_DURATION - (now - timestamp)) / 1000 / 60}分钟`)
return isValid
} catch (error) {
console.error('[SystemConfig] 缓存检查失败:', error)
return false
}
},
// 获取缓存的数据
cachedConfig(): any {
if (!isClient() || !this.isCacheValid) return null
try {
const cacheData = localStorage.getItem(CACHE_KEY)
if (cacheData) {
const parsed = JSON.parse(cacheData) as CacheData
console.log('[SystemConfig] 使用缓存数据')
return parsed.config
}
} catch (error) {
console.error('[SystemConfig] 读取缓存失败:', error)
this.clearCache()
}
return null
},
// 获取缓存剩余时间(秒)
cacheTimeRemaining(): number {
if (!isClient()) return 0
try {
const cacheTimestamp = localStorage.getItem(CACHE_TIMESTAMP_KEY)
if (!cacheTimestamp) return 0
const timestamp = parseInt(cacheTimestamp)
const now = Date.now()
const remaining = Math.max(0, CACHE_DURATION - (now - timestamp))
return Math.floor(remaining / 1000)
} catch (error) {
return 0
}
}
},
actions: {
// 清除缓存
clearCache() {
if (!isClient()) return
try {
localStorage.removeItem(CACHE_KEY)
localStorage.removeItem(CACHE_TIMESTAMP_KEY)
console.log('[SystemConfig] 缓存已清除')
} catch (error) {
console.error('[SystemConfig] 清除缓存失败:', error)
}
},
// 保存到缓存
saveToCache(config: any) {
if (!isClient()) return
try {
const cacheData: CacheData = {
config,
timestamp: Date.now(),
version: '1.0'
}
localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData))
localStorage.setItem(CACHE_TIMESTAMP_KEY, cacheData.timestamp.toString())
console.log('[SystemConfig] 配置已缓存有效期30分钟')
} catch (error) {
console.error('[SystemConfig] 保存缓存失败:', error)
}
},
// 从缓存加载
loadFromCache(): boolean {
if (!isClient()) return false
const cachedConfig = this.cachedConfig
if (cachedConfig) {
this.config = cachedConfig
this.initialized = true
// 从缓存时间戳设置 lastFetchTime
const cacheTimestamp = localStorage.getItem(CACHE_TIMESTAMP_KEY)
if (cacheTimestamp) {
this.lastFetchTime = parseInt(cacheTimestamp)
} else {
this.lastFetchTime = Date.now()
}
console.log('[SystemConfig] 从缓存加载配置成功')
return true
}
return false
},
// 初始化配置(带缓存支持)
async initConfig(force = false, useAdminApi = false) {
// 如果已经初始化且不强制刷新,直接返回
if (this.initialized && !force) {
console.log('[SystemConfig] 配置已初始化,直接返回')
return
}
// 如果不强制刷新,先尝试从缓存加载
if (!force && this.loadFromCache()) {
return
}
// 防止重复请求
if (this.isLoading) {
console.log('[SystemConfig] 正在加载中,等待完成...')
return
}
this.isLoading = true
this.error = null
try {
console.log(`[SystemConfig] 开始获取配置 (force: ${force}, useAdminApi: ${useAdminApi})`)
// 根据上下文选择API管理员页面使用管理员API其他页面使用公开API
const apiUrl = useAdminApi ? '/system/config' : '/public/system-config'
const response = await useApiFetch(apiUrl)
// console.log('Store API响应:', response) // 调试信息
// 使用parseApiResponse正确解析API响应
const data = parseApiResponse(response)
// console.log('Store 处理后的数据:', data) // 调试信息
// console.log('Store 自动处理状态:', data.auto_process_ready_resources)
// console.log('Store 自动转存状态:', data.auto_transfer_enabled)
this.config = data
this.initialized = true
} catch (e) {
console.error('Store 获取系统配置失败:', e) // 调试信息
// 可根据需要处理错误
this.lastFetchTime = Date.now()
this.isLoading = false
// 保存到缓存(仅在客户端)
this.saveToCache(data)
console.log('[SystemConfig] 配置获取并缓存成功')
console.log('[SystemConfig] 自动处理状态:', data.auto_process_ready_resources)
console.log('[SystemConfig] 自动转存状态:', data.auto_transfer_enabled)
} catch (error) {
this.isLoading = false
this.error = error instanceof Error ? error.message : '获取配置失败'
console.error('[SystemConfig] 获取系统配置失败:', error)
// 如果网络请求失败,尝试使用过期的缓存作为降级方案
if (!force) {
try {
const expiredCache = localStorage.getItem(CACHE_KEY)
if (expiredCache) {
const parsed = JSON.parse(expiredCache) as CacheData
this.config = parsed.config
this.initialized = true
console.log('[SystemConfig] 网络请求失败,使用过期缓存作为降级方案')
return
}
} catch (cacheError) {
console.error('[SystemConfig] 降级缓存方案也失败:', cacheError)
}
}
this.config = null
this.initialized = false
}
},
// 强制刷新配置
async refreshConfig(useAdminApi = false) {
console.log('[SystemConfig] 强制刷新配置')
this.clearCache()
await this.initConfig(true, useAdminApi)
},
// 检查并自动刷新缓存(如果即将过期)
async checkAndRefreshCache(useAdminApi = false) {
if (!isClient()) return
const timeRemaining = this.cacheTimeRemaining
// 如果缓存剩余时间少于5分钟自动刷新
if (timeRemaining > 0 && timeRemaining < 5 * 60) {
console.log(`[SystemConfig] 缓存即将过期(剩余${timeRemaining}秒),自动刷新`)
await this.refreshConfig(useAdminApi)
}
},
// 手动设置配置(用于管理员更新配置后)
setConfig(newConfig: any) {
this.config = newConfig
this.initialized = true
this.lastFetchTime = Date.now()
// 更新缓存
this.saveToCache(newConfig)
console.log('[SystemConfig] 配置已手动更新并缓存')
},
// 获取配置状态信息
getStatus() {
return {
initialized: this.initialized,
isLoading: this.isLoading,
error: this.error,
lastFetchTime: this.lastFetchTime,
cacheTimeRemaining: this.cacheTimeRemaining,
isCacheValid: this.isCacheValid
}
}
}
})
})