mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-24 19:12:52 +08:00
update: add site_url config
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -123,4 +123,11 @@ dist/
|
||||
.dockerignore
|
||||
|
||||
# Air live reload
|
||||
tmp/
|
||||
tmp/
|
||||
|
||||
#
|
||||
data/
|
||||
.claude/
|
||||
main
|
||||
output.zip
|
||||
CLAUDE.md
|
||||
@@ -112,6 +112,8 @@ func SystemConfigToResponse(configs []entity.SystemConfig) *dto.SystemConfigResp
|
||||
response.TelegramQrImage = config.Value
|
||||
case entity.ConfigKeyQrCodeStyle:
|
||||
response.QrCodeStyle = config.Value
|
||||
case entity.ConfigKeyWebsiteURL:
|
||||
response.SiteURL = config.Value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +273,10 @@ func RequestToSystemConfig(req *dto.SystemConfigRequest) []entity.SystemConfig {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyQrCodeStyle, Value: *req.QrCodeStyle, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyQrCodeStyle)
|
||||
}
|
||||
if req.SiteURL != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyWebsiteURL, Value: *req.SiteURL, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyWebsiteURL)
|
||||
}
|
||||
|
||||
// 记录更新的配置项
|
||||
if len(updatedKeys) > 0 {
|
||||
@@ -298,6 +304,7 @@ func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]inte
|
||||
entity.ConfigResponseFieldMaintenanceMode: false,
|
||||
entity.ConfigResponseFieldEnableRegister: true, // 默认开启注册功能
|
||||
entity.ConfigResponseFieldThirdPartyStatsCode: "",
|
||||
entity.ConfigResponseFieldWebsiteURL: "",
|
||||
}
|
||||
|
||||
// 将键值对转换为map,过滤掉敏感配置
|
||||
@@ -353,6 +360,8 @@ 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.ConfigKeyWebsiteURL:
|
||||
response[entity.ConfigResponseFieldWebsiteURL] = config.Value
|
||||
case entity.ConfigKeyAutoProcessReadyResources:
|
||||
if val, err := strconv.ParseBool(config.Value); err == nil {
|
||||
response["auto_process_ready_resources"] = val
|
||||
@@ -420,5 +429,6 @@ func getDefaultConfigResponse() *dto.SystemConfigResponse {
|
||||
WechatSearchImage: entity.ConfigDefaultWechatSearchImage,
|
||||
TelegramQrImage: entity.ConfigDefaultTelegramQrImage,
|
||||
QrCodeStyle: entity.ConfigDefaultQrCodeStyle,
|
||||
SiteURL: entity.ConfigDefaultWebsiteURL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ type SystemConfigRequest struct {
|
||||
WechatSearchImage *string `json:"wechat_search_image,omitempty"`
|
||||
TelegramQrImage *string `json:"telegram_qr_image,omitempty"`
|
||||
QrCodeStyle *string `json:"qr_code_style,omitempty"`
|
||||
|
||||
// 网站URL配置
|
||||
SiteURL *string `json:"site_url,omitempty"`
|
||||
}
|
||||
|
||||
// SystemConfigResponse 系统配置响应
|
||||
@@ -106,6 +109,9 @@ type SystemConfigResponse struct {
|
||||
WechatSearchImage string `json:"wechat_search_image"`
|
||||
TelegramQrImage string `json:"telegram_qr_image"`
|
||||
QrCodeStyle string `json:"qr_code_style"`
|
||||
|
||||
// 网站URL配置
|
||||
SiteURL string `json:"site_url"`
|
||||
}
|
||||
|
||||
// SystemConfigItem 单个配置项
|
||||
|
||||
@@ -170,6 +170,9 @@ const (
|
||||
ConfigResponseFieldWechatSearchImage = "wechat_search_image"
|
||||
ConfigResponseFieldTelegramQrImage = "telegram_qr_image"
|
||||
ConfigResponseFieldQrCodeStyle = "qr_code_style"
|
||||
|
||||
// 网站URL配置字段
|
||||
ConfigResponseFieldWebsiteURL = "site_url"
|
||||
)
|
||||
|
||||
// ConfigDefaultValue 配置默认值常量
|
||||
@@ -245,4 +248,7 @@ const (
|
||||
ConfigDefaultWechatSearchImage = ""
|
||||
ConfigDefaultTelegramQrImage = ""
|
||||
ConfigDefaultQrCodeStyle = "Plain"
|
||||
|
||||
// 网站URL配置默认值
|
||||
ConfigDefaultWebsiteURL = ""
|
||||
)
|
||||
|
||||
@@ -143,6 +143,7 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
|
||||
{Key: entity.ConfigKeyWechatSearchImage, Value: entity.ConfigDefaultWechatSearchImage, Type: entity.ConfigTypeString},
|
||||
{Key: entity.ConfigKeyTelegramQrImage, Value: entity.ConfigDefaultTelegramQrImage, Type: entity.ConfigTypeString},
|
||||
{Key: entity.ConfigKeyQrCodeStyle, Value: entity.ConfigDefaultQrCodeStyle, Type: entity.ConfigTypeString},
|
||||
{Key: entity.ConfigKeyWebsiteURL, Value: entity.ConfigDefaultWebsiteURL, Type: entity.ConfigTypeString},
|
||||
}
|
||||
|
||||
createStart := utils.GetCurrentTime()
|
||||
@@ -189,6 +190,7 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
|
||||
entity.ConfigKeyEnableFloatButtons: {Key: entity.ConfigKeyEnableFloatButtons, Value: entity.ConfigDefaultEnableFloatButtons, Type: entity.ConfigTypeBool},
|
||||
entity.ConfigKeyWechatSearchImage: {Key: entity.ConfigKeyWechatSearchImage, Value: entity.ConfigDefaultWechatSearchImage, Type: entity.ConfigTypeString},
|
||||
entity.ConfigKeyTelegramQrImage: {Key: entity.ConfigKeyTelegramQrImage, Value: entity.ConfigDefaultTelegramQrImage, Type: entity.ConfigTypeString},
|
||||
entity.ConfigKeyWebsiteURL: {Key: entity.ConfigKeyWebsiteURL, Value: entity.ConfigDefaultWebsiteURL, Type: entity.ConfigTypeString},
|
||||
}
|
||||
|
||||
// 检查现有配置中是否有缺失的配置项
|
||||
|
||||
@@ -431,7 +431,7 @@ const getCategoryName = (categoryId: number) => {
|
||||
|
||||
// 获取平台名称
|
||||
const getPlatformName = (platformId: number) => {
|
||||
console.log('platformId', platformId, platformsData.value)
|
||||
// console.log('platformId', platformId, platformsData.value)
|
||||
const platform = (platformsData.value as any)?.find((plat: any) => plat.id === platformId)
|
||||
return platform?.remark || platform?.name || '未知平台'
|
||||
}
|
||||
@@ -449,22 +449,22 @@ const fetchData = async () => {
|
||||
// 添加分类筛选
|
||||
if (selectedCategory.value) {
|
||||
params.category_id = selectedCategory.value
|
||||
console.log('添加分类筛选:', selectedCategory.value)
|
||||
// console.log('添加分类筛选:', selectedCategory.value)
|
||||
}
|
||||
|
||||
// 添加平台筛选
|
||||
if (selectedPlatform.value) {
|
||||
params.pan_id = selectedPlatform.value
|
||||
console.log('添加平台筛选:', selectedPlatform.value)
|
||||
// console.log('添加平台筛选:', selectedPlatform.value)
|
||||
}
|
||||
|
||||
console.log('请求参数:', params)
|
||||
console.log('pageSize:', pageSize.value)
|
||||
console.log('selectedCategory:', selectedCategory.value)
|
||||
console.log('selectedPlatform:', selectedPlatform.value)
|
||||
// console.log('请求参数:', params)
|
||||
// console.log('pageSize:', pageSize.value)
|
||||
// console.log('selectedCategory:', selectedCategory.value)
|
||||
// console.log('selectedPlatform:', selectedPlatform.value)
|
||||
const response = await resourceApi.getResources(params) as any
|
||||
console.log('API响应:', response)
|
||||
console.log('返回的资源数量:', response?.data?.length || 0)
|
||||
// console.log('API响应:', response)
|
||||
// console.log('返回的资源数量:', response?.data?.length || 0)
|
||||
|
||||
if (response && response.data) {
|
||||
// 处理嵌套的data结构:{data: {data: [...], total: ...}}
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
|
||||
<!-- Sitemap配置 -->
|
||||
<div class="mb-6 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h4 class="font-medium text-gray-900 dark:text-white mb-2">自动生成功能</h4>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -302,6 +302,29 @@
|
||||
<template #unchecked>已关闭</template>
|
||||
</n-switch>
|
||||
</div>
|
||||
|
||||
<!-- 站点URL显示 -->
|
||||
<div class="border-t border-gray-200 dark:border-gray-600 pt-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h5 class="font-medium text-gray-900 dark:text-white mb-1">当前站点URL</h5>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Sitemap生成时将使用此URL作为基础域名
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div v-if="systemConfig?.site_url" class="text-sm font-medium text-blue-600 dark:text-blue-400 mb-2">
|
||||
{{ systemConfig.site_url }}
|
||||
</div>
|
||||
<div v-else class="text-sm text-yellow-600 dark:text-yellow-400 mb-2">
|
||||
未配置
|
||||
</div>
|
||||
<NuxtLink to="/admin/site-config" class="text-xs text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 underline">
|
||||
前往配置
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sitemap统计 -->
|
||||
@@ -678,12 +701,20 @@ const generateSitemap = async () => {
|
||||
generateStatus.value = '正在启动生成任务...'
|
||||
|
||||
try {
|
||||
// 使用已经加载的系统配置
|
||||
const siteUrl = systemConfig.value?.site_url || ''
|
||||
if (!siteUrl) {
|
||||
message.warning('请先在站点配置中设置站点URL,然后再生成Sitemap')
|
||||
isGenerating.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const sitemapApi = useSitemapApi()
|
||||
const response = await sitemapApi.generateSitemap()
|
||||
const response = await sitemapApi.generateSitemap({ site_url: siteUrl })
|
||||
|
||||
if (response) {
|
||||
generateStatus.value = response.message || '生成任务已启动'
|
||||
message.success('Sitemap生成任务已启动')
|
||||
message.success(`Sitemap生成任务已启动,使用站点URL: ${siteUrl}`)
|
||||
// 更新统计信息
|
||||
sitemapStats.value.total_resources = response.total_resources || 0
|
||||
sitemapStats.value.total_pages = response.total_pages || 0
|
||||
@@ -715,8 +746,24 @@ const viewSitemap = () => {
|
||||
window.open('/sitemap.xml', '_blank')
|
||||
}
|
||||
|
||||
// 获取系统配置
|
||||
const systemConfig = ref<any>(null)
|
||||
|
||||
// 加载系统配置
|
||||
const loadSystemConfig = async () => {
|
||||
try {
|
||||
const { useSystemConfigStore } = await import('~/stores/systemConfig')
|
||||
const systemConfigStore = useSystemConfigStore()
|
||||
await systemConfigStore.initConfig(true, true)
|
||||
systemConfig.value = systemConfigStore.config
|
||||
} catch (error) {
|
||||
console.error('获取系统配置失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
await loadSystemConfig()
|
||||
loadLinkList()
|
||||
await loadSitemapConfig()
|
||||
await refreshSitemapStatus()
|
||||
|
||||
@@ -34,6 +34,18 @@
|
||||
require-mark-placement="right-hanging"
|
||||
>
|
||||
<div class="space-y-6">
|
||||
<!-- 站点URL -->
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-base font-semibold text-gray-800 dark:text-gray-200">站点URL</label>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">网站的基础URL,用于生成sitemap等,请包含协议名(如:https://example.com)</span>
|
||||
</div>
|
||||
<n-input
|
||||
v-model:value="configForm.site_url"
|
||||
placeholder="请输入站点URL,如:https://example.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 网站标题 -->
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
@@ -298,6 +310,7 @@ interface Announcement {
|
||||
|
||||
// 配置表单数据类型
|
||||
interface SiteConfigForm {
|
||||
site_url: string
|
||||
site_title: string
|
||||
site_description: string
|
||||
keywords: string
|
||||
@@ -362,6 +375,7 @@ const {
|
||||
},
|
||||
// 字段映射:前端字段名 -> 后端字段名
|
||||
fieldMapping: {
|
||||
site_url: 'site_url',
|
||||
site_title: 'site_title',
|
||||
site_description: 'site_description',
|
||||
keywords: 'keywords',
|
||||
@@ -383,6 +397,7 @@ const {
|
||||
|
||||
// 配置表单数据
|
||||
const configForm = ref<SiteConfigForm>({
|
||||
site_url: '',
|
||||
site_title: '',
|
||||
site_description: '',
|
||||
keywords: '',
|
||||
@@ -424,6 +439,7 @@ const fetchConfig = async () => {
|
||||
|
||||
if (response) {
|
||||
const configData = {
|
||||
site_url: response.site_url || '',
|
||||
site_title: response.site_title || '',
|
||||
site_description: response.site_description || '',
|
||||
keywords: response.keywords || '',
|
||||
|
||||
@@ -608,24 +608,39 @@ watch(systemConfigError, (error) => {
|
||||
// 从 SSR 数据中获取值
|
||||
const safeResources = computed(() => {
|
||||
const data = resourcesData.value as any
|
||||
// console.log('原始API数据结构:', JSON.stringify(data, null, 2))
|
||||
let resources: any[] = []
|
||||
|
||||
// 处理嵌套的data结构:{data: {data: [...], total: ...}}
|
||||
if (data?.data?.data && Array.isArray(data.data.data)) {
|
||||
const resources = data.data.data
|
||||
resources = data.data.data
|
||||
console.log('第一层嵌套资源:', resources)
|
||||
return resources
|
||||
}
|
||||
// 处理直接的data结构:{data: [...], total: ...}
|
||||
if (data?.data && Array.isArray(data.data)) {
|
||||
const resources = data.data
|
||||
else if (data?.data && Array.isArray(data.data)) {
|
||||
resources = data.data
|
||||
// console.log('第二层嵌套资源:', resources)
|
||||
return resources
|
||||
}
|
||||
// 处理直接的数组结构
|
||||
if (Array.isArray(data)) {
|
||||
else if (Array.isArray(data)) {
|
||||
resources = data
|
||||
// console.log('直接数组结构:', data)
|
||||
return data
|
||||
}
|
||||
|
||||
// 根据 key 字段去重
|
||||
if (resources.length > 0) {
|
||||
const keyMap = new Map()
|
||||
const deduplicatedResources: any[] = []
|
||||
|
||||
for (const resource of resources) {
|
||||
const key = resource.key
|
||||
if (!keyMap.has(key)) {
|
||||
keyMap.set(key, true)
|
||||
deduplicatedResources.push(resource)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(`去重前: ${resources.length} 个资源, 去重后: ${deduplicatedResources.length} 个资源`)
|
||||
return deduplicatedResources
|
||||
}
|
||||
|
||||
// console.log('未匹配到任何数据结构')
|
||||
|
||||
@@ -203,9 +203,6 @@
|
||||
? (detectionMethods[resource.id] === 'unsupported' ? '不支持检测' : (detectionResults[resource.id] ? '有效' : '无效'))
|
||||
: '不支持检测' }}
|
||||
</span>
|
||||
<span v-if="resource.save_url" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-700 dark:bg-blue-500/20 dark:text-blue-100">
|
||||
已转存
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user