mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 03:15:04 +08:00
update: detail ui
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
21
web/ecosystem.config.cjs
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user