update: detail ui

This commit is contained in:
ctwj
2025-11-18 00:49:57 +08:00
parent c50282bec8
commit 5dc431ab24
8 changed files with 86 additions and 82 deletions

View File

@@ -37,6 +37,18 @@ func ToResourceResponse(resource *entity.Resource) dto.ResourceResponse {
response.CategoryName = resource.Category.Name response.CategoryName = resource.Category.Name
} }
// 设置平台信息
if resource.Pan.ID != 0 {
panResponse := dto.PanResponse{
ID: resource.Pan.ID,
Name: resource.Pan.Name,
Key: resource.Pan.Key,
Icon: resource.Pan.Icon,
Remark: resource.Pan.Remark,
}
response.Pan = &panResponse
}
// 转换标签 // 转换标签
response.Tags = make([]dto.TagResponse, len(resource.Tags)) response.Tags = make([]dto.TagResponse, len(resource.Tags))
for i, tag := range resource.Tags { for i, tag := range resource.Tags {

View File

@@ -280,39 +280,27 @@ func RequestToSystemConfig(req *dto.SystemConfigRequest) []entity.SystemConfig {
return configs return configs
} }
// SystemConfigToPublicResponse 返回不含 api_token 的系统配置响应 // SystemConfigToPublicResponse 返回不含敏感配置的系统配置响应
func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]interface{} { func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]interface{} {
response := map[string]interface{}{ response := map[string]interface{}{
entity.ConfigResponseFieldID: 0, entity.ConfigResponseFieldID: 0,
entity.ConfigResponseFieldCreatedAt: utils.GetCurrentTimeString(), entity.ConfigResponseFieldCreatedAt: utils.GetCurrentTimeString(),
entity.ConfigResponseFieldUpdatedAt: utils.GetCurrentTimeString(), entity.ConfigResponseFieldUpdatedAt: utils.GetCurrentTimeString(),
entity.ConfigResponseFieldSiteTitle: entity.ConfigDefaultSiteTitle, entity.ConfigResponseFieldSiteTitle: entity.ConfigDefaultSiteTitle,
entity.ConfigResponseFieldSiteDescription: entity.ConfigDefaultSiteDescription, entity.ConfigResponseFieldSiteDescription: entity.ConfigDefaultSiteDescription,
entity.ConfigResponseFieldKeywords: entity.ConfigDefaultKeywords, entity.ConfigResponseFieldKeywords: entity.ConfigDefaultKeywords,
entity.ConfigResponseFieldAuthor: entity.ConfigDefaultAuthor, entity.ConfigResponseFieldAuthor: entity.ConfigDefaultAuthor,
entity.ConfigResponseFieldCopyright: entity.ConfigDefaultCopyright, entity.ConfigResponseFieldCopyright: entity.ConfigDefaultCopyright,
"site_logo": "", "site_logo": "",
entity.ConfigResponseFieldAutoProcessReadyResources: false, entity.ConfigResponseFieldAdKeywords: "",
entity.ConfigResponseFieldAutoProcessInterval: 30, entity.ConfigResponseFieldAutoInsertAd: "",
entity.ConfigResponseFieldAutoTransferEnabled: false, entity.ConfigResponseFieldPageSize: 100,
entity.ConfigResponseFieldAutoTransferLimitDays: 0, entity.ConfigResponseFieldMaintenanceMode: false,
entity.ConfigResponseFieldAutoTransferMinSpace: 100, entity.ConfigResponseFieldEnableRegister: true, // 默认开启注册功能
entity.ConfigResponseFieldAutoFetchHotDramaEnabled: false, entity.ConfigResponseFieldThirdPartyStatsCode: "",
entity.ConfigResponseFieldForbiddenWords: "",
entity.ConfigResponseFieldAdKeywords: "",
entity.ConfigResponseFieldAutoInsertAd: "",
entity.ConfigResponseFieldPageSize: 100,
entity.ConfigResponseFieldMaintenanceMode: false,
entity.ConfigResponseFieldEnableRegister: true, // 默认开启注册功能
entity.ConfigResponseFieldThirdPartyStatsCode: "",
entity.ConfigResponseFieldMeilisearchEnabled: false,
entity.ConfigResponseFieldMeilisearchHost: "localhost",
entity.ConfigResponseFieldMeilisearchPort: "7700",
entity.ConfigResponseFieldMeilisearchMasterKey: "",
entity.ConfigResponseFieldMeilisearchIndexName: "resources",
} }
// 将键值对转换为map // 将键值对转换为map,过滤掉敏感配置
for _, config := range configs { for _, config := range configs {
switch config.Key { switch config.Key {
case entity.ConfigKeySiteTitle: case entity.ConfigKeySiteTitle:
@@ -327,32 +315,6 @@ func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]inte
response[entity.ConfigResponseFieldCopyright] = config.Value response[entity.ConfigResponseFieldCopyright] = config.Value
case entity.ConfigKeySiteLogo: case entity.ConfigKeySiteLogo:
response["site_logo"] = config.Value response["site_logo"] = config.Value
case entity.ConfigKeyAutoProcessReadyResources:
if val, err := strconv.ParseBool(config.Value); err == nil {
response[entity.ConfigResponseFieldAutoProcessReadyResources] = val
}
case entity.ConfigKeyAutoProcessInterval:
if val, err := strconv.Atoi(config.Value); err == nil {
response[entity.ConfigResponseFieldAutoProcessInterval] = val
}
case entity.ConfigKeyAutoTransferEnabled:
if val, err := strconv.ParseBool(config.Value); err == nil {
response[entity.ConfigResponseFieldAutoTransferEnabled] = val
}
case entity.ConfigKeyAutoTransferLimitDays:
if val, err := strconv.Atoi(config.Value); err == nil {
response[entity.ConfigResponseFieldAutoTransferLimitDays] = val
}
case entity.ConfigKeyAutoTransferMinSpace:
if val, err := strconv.Atoi(config.Value); err == nil {
response[entity.ConfigResponseFieldAutoTransferMinSpace] = val
}
case entity.ConfigKeyAutoFetchHotDramaEnabled:
if val, err := strconv.ParseBool(config.Value); err == nil {
response[entity.ConfigResponseFieldAutoFetchHotDramaEnabled] = val
}
case entity.ConfigKeyForbiddenWords:
response[entity.ConfigResponseFieldForbiddenWords] = config.Value
case entity.ConfigKeyAdKeywords: case entity.ConfigKeyAdKeywords:
response[entity.ConfigResponseFieldAdKeywords] = config.Value response[entity.ConfigResponseFieldAdKeywords] = config.Value
case entity.ConfigKeyAutoInsertAd: case entity.ConfigKeyAutoInsertAd:
@@ -371,18 +333,6 @@ func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]inte
} }
case entity.ConfigKeyThirdPartyStatsCode: case entity.ConfigKeyThirdPartyStatsCode:
response[entity.ConfigResponseFieldThirdPartyStatsCode] = config.Value response[entity.ConfigResponseFieldThirdPartyStatsCode] = config.Value
case entity.ConfigKeyMeilisearchEnabled:
if val, err := strconv.ParseBool(config.Value); err == nil {
response[entity.ConfigResponseFieldMeilisearchEnabled] = val
}
case entity.ConfigKeyMeilisearchHost:
response[entity.ConfigResponseFieldMeilisearchHost] = config.Value
case entity.ConfigKeyMeilisearchPort:
response[entity.ConfigResponseFieldMeilisearchPort] = config.Value
case entity.ConfigKeyMeilisearchMasterKey:
response[entity.ConfigResponseFieldMeilisearchMasterKey] = config.Value
case entity.ConfigKeyMeilisearchIndexName:
response[entity.ConfigResponseFieldMeilisearchIndexName] = config.Value
case entity.ConfigKeyEnableAnnouncements: case entity.ConfigKeyEnableAnnouncements:
if val, err := strconv.ParseBool(config.Value); err == nil { if val, err := strconv.ParseBool(config.Value); err == nil {
response["enable_announcements"] = val response["enable_announcements"] = val
@@ -403,6 +353,21 @@ func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]inte
response["telegram_qr_image"] = config.Value response["telegram_qr_image"] = config.Value
case entity.ConfigKeyQrCodeStyle: case entity.ConfigKeyQrCodeStyle:
response["qr_code_style"] = config.Value response["qr_code_style"] = config.Value
// 跳过不需要返回给公众的配置
case entity.ConfigKeyAutoProcessReadyResources:
case entity.ConfigKeyAutoProcessInterval:
case entity.ConfigKeyAutoTransferEnabled:
case entity.ConfigKeyAutoTransferLimitDays:
case entity.ConfigKeyAutoTransferMinSpace:
case entity.ConfigKeyAutoFetchHotDramaEnabled:
case entity.ConfigKeyMeilisearchEnabled:
case entity.ConfigKeyMeilisearchHost:
case entity.ConfigKeyMeilisearchPort:
case entity.ConfigKeyMeilisearchMasterKey:
case entity.ConfigKeyMeilisearchIndexName:
case entity.ConfigKeyForbiddenWords:
// 这些配置不返回给公众
continue
} }
} }

View File

@@ -33,6 +33,7 @@ type ResourceResponse struct {
ErrorMsg string `json:"error_msg"` ErrorMsg string `json:"error_msg"`
SyncedToMeilisearch bool `json:"synced_to_meilisearch"` SyncedToMeilisearch bool `json:"synced_to_meilisearch"`
SyncedAt *time.Time `json:"synced_at"` SyncedAt *time.Time `json:"synced_at"`
Pan *PanResponse `json:"pan,omitempty"` // 平台信息
// 高亮字段 // 高亮字段
TitleHighlight string `json:"title_highlight,omitempty"` TitleHighlight string `json:"title_highlight,omitempty"`
DescriptionHighlight string `json:"description_highlight,omitempty"` DescriptionHighlight string `json:"description_highlight,omitempty"`

View File

@@ -2,6 +2,8 @@ package repo
import ( import (
"fmt" "fmt"
"strconv"
"strings"
"time" "time"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
@@ -243,6 +245,23 @@ func (r *ResourceRepositoryImpl) SearchWithFilters(params map[string]interface{}
Where("resource_tags.tag_id = ?", tagEntity.ID) Where("resource_tags.tag_id = ?", tagEntity.ID)
} }
} }
case "tag_ids": // 添加tag_ids参数支持标签ID列表
if tagIdsStr, ok := value.(string); ok && tagIdsStr != "" {
// 将逗号分隔的标签ID字符串转换为整数ID数组
tagIdStrs := strings.Split(tagIdsStr, ",")
var tagIds []uint
for _, idStr := range tagIdStrs {
idStr = strings.TrimSpace(idStr) // 去除空格
if id, err := strconv.ParseUint(idStr, 10, 32); err == nil {
tagIds = append(tagIds, uint(id))
}
}
if len(tagIds) > 0 {
// 通过中间表查找包含任一标签的资源
db = db.Joins("JOIN resource_tags ON resources.id = resource_tags.resource_id").
Where("resource_tags.tag_id IN ?", tagIds)
}
}
case "pan_id": // 添加pan_id参数支持 case "pan_id": // 添加pan_id参数支持
if panID, ok := value.(uint); ok { if panID, ok := value.(uint); ok {
db = db.Where("pan_id = ?", panID) db = db.Where("pan_id = ?", panID)

View File

@@ -8,13 +8,6 @@
class="max-w-lg w-full" class="max-w-lg w-full"
:style="{ maxWidth: '95vw' }" :style="{ maxWidth: '95vw' }"
> >
<template #header-extra>
<n-button size="small" circle @click="handleClose">
<template #icon>
<i class="fas fa-times"></i>
</template>
</n-button>
</template>
<div class="space-y-4"> <div class="space-y-4">
<div class="bg-blue-50 dark:bg-blue-500/10 border border-blue-200 dark:border-blue-400/30 rounded-lg p-4"> <div class="bg-blue-50 dark:bg-blue-500/10 border border-blue-200 dark:border-blue-400/30 rounded-lg p-4">

View File

@@ -8,13 +8,6 @@
class="max-w-md w-full" class="max-w-md w-full"
:style="{ maxWidth: '90vw' }" :style="{ maxWidth: '90vw' }"
> >
<template #header-extra>
<n-button size="small" circle @click="handleClose">
<template #icon>
<i class="fas fa-times"></i>
</template>
</n-button>
</template>
<div class="space-y-4"> <div class="space-y-4">
<div class="text-gray-600 dark:text-gray-300 text-sm"> <div class="text-gray-600 dark:text-gray-300 text-sm">

21
web/ecosystem.config.cjs Normal file
View File

@@ -0,0 +1,21 @@
module.exports = {
apps: [
{
name: 'urldb-nuxt',
port: '3030',
exec_mode: 'cluster',
instances: 'max', // 使用所有可用的CPU核心
script: './.output/server/index.mjs',
error_file: './logs/err.log',
out_file: './logs/out.log',
log_file: './logs/combined.log',
time: true,
env: {
NODE_ENV: 'production',
HOST: '0.0.0.0',
PORT: 3030,
NUXT_PUBLIC_API_SERVER: 'http://localhost:8080/api'
}
}
]
};

View File

@@ -147,7 +147,7 @@
<span v-html="resource.pan?.icon" class="text-lg"></span> <span v-html="resource.pan?.icon" class="text-lg"></span>
</div> </div>
<div> <div>
<div class="font-medium text-gray-900 dark:text-gray-100">{{ resource.pan?.name || '未知平台' }}</div> <div class="font-medium text-gray-900 dark:text-gray-100">{{ resource.pan?.remark || '未知平台' }}</div>
<div class="flex items-center gap-2 mt-1"> <div class="flex items-center gap-2 mt-1">
<span <span
class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium"