From e1572e1327cc188b3c8acb3e02a22da36d0341b0 Mon Sep 17 00:00:00 2001 From: ice-a Date: Fri, 16 Oct 2026 19:06:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=BE=E5=AE=BD=20MongoDB=20?= =?UTF-8?q?=E8=B6=85=E6=97=B6(Atlas)=20+=20=E7=83=AD=E6=90=9C=203D=20?= =?UTF-8?q?=E8=AF=8D=E4=BA=91(Top10)=20+=20i18n=20=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 9 +- app/components/KeywordCloud3D.vue | 147 ++++++++++++++++++++++++++++++ app/pages/index.vue | 18 +++- app/utils/i18n-dict.ts | 8 +- server/api/hot.get.ts | 21 +++++ server/utils/mongo.ts | 6 +- 6 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 app/components/KeywordCloud3D.vue create mode 100644 server/api/hot.get.ts diff --git a/.env.example b/.env.example index d2a76c8..39781fd 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,7 @@ # MongoDB 连接(留空则回落到本地默认,配置/历史不持久化) +# 本地: mongodb://127.0.0.1:27017 +# 云端 Atlas 示例(把 换成真实密码,数据库会建在 MONGODB_DB 指定的库,不会落到 test): +# MONGODB_URI=mongodb://devops:@ac-eqksaup-shard-00-00.utlbkzb.mongodb.net:27017,ac-eqksaup-shard-00-01.utlbkzb.mongodb.net:27017,ac-eqksaup-shard-00-02.utlbkzb.mongodb.net:27017/?ssl=true&replicaSet=atlas-ofsrbz-shard-0&authSource=admin&appName=devops MONGODB_URI=mongodb://127.0.0.1:27017 MONGODB_DB=torrent_console @@ -11,9 +14,9 @@ SEARCH_TIMEOUT=12000 METADATA_TIMEOUT=30000 # 各搜索源基地址(一般无需修改) -APIBAY_BASE=https://apibay.org -NYAA_BASE=https://nyaa.si -CILISOUSUO_BASE=https://cilisousuo.cc +APIBAY_BASE= +NYAA_BASE= +CILISOUSUO_BASE= # 生产运行监听(仅直接运行 .output 时需要) HOST=0.0.0.0 diff --git a/app/components/KeywordCloud3D.vue b/app/components/KeywordCloud3D.vue new file mode 100644 index 0000000..9a7edb2 --- /dev/null +++ b/app/components/KeywordCloud3D.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/app/pages/index.vue b/app/pages/index.vue index 75660ee..ff09e4e 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -6,6 +6,17 @@ type Tab = 'search' | 'file' | 'magnet' const route = useRoute() const health = useHealth() +/* ---------------- 热搜 3D 词云 ---------------- */ +const { data: hotData, refresh: refreshHot } = await useFetch<{ keywords: { keyword: string; count: number }[] }>('/api/hot', { + query: { limit: 10 } +}) +const hotWords = computed(() => (hotData.value?.keywords || []).map((k) => ({ text: k.keyword, weight: k.count }))) + +function onHotSelect(kw: string) { + if (tab.value !== 'search') tab.value = 'search' + doSearch(kw) +} + const tab = ref('search') /* ---------------- 关键词搜索 ---------------- */ @@ -71,6 +82,7 @@ async function doSearch(term?: string) { pushToast('err', '搜索失败', errorMessage(err)) } finally { searching.value = false + refreshHot() } } @@ -141,7 +153,7 @@ function previewLink(item: TorrentView) {
- MongoDB 未连接,搜索记录与种子缓存不会被保存(其余功能可用)。启动本地 MongoDB 后刷新即可: + MongoDB 未连接,搜索记录与种子缓存不会被保存(其余功能可用)。在 .env 配置正确的 MONGODB_URI(云端实例请填真实密码)后刷新即可: {{ health.mongo.uri }}
@@ -177,6 +189,10 @@ function previewLink(item: TorrentView) { + + + +
{{ response.results.length }} 条结果 · {{ response.durationMs }}ms · diff --git a/app/utils/i18n-dict.ts b/app/utils/i18n-dict.ts index e210db0..266f9fb 100644 --- a/app/utils/i18n-dict.ts +++ b/app/utils/i18n-dict.ts @@ -219,7 +219,13 @@ export const zh2en: Record = { // ===== 输入框占位符 ===== '输入片名 / 剧名 / 关键词,回车搜索': 'Movie / show / keyword, press Enter to search', - 'http://127.0.0.1:7890 / 留空直连': 'http://127.0.0.1:7890 / blank for direct' + 'http://127.0.0.1:7890 / 留空直连': 'http://127.0.0.1:7890 / blank for direct', + + // ===== 热词云 / 搜索页提示 ===== + '点击关键词直接搜索': 'Click a keyword to search', + '已写入 MongoDB': 'Saved to MongoDB', + 'MongoDB 未连接,未记录': 'MongoDB not connected, not recorded', + 'MongoDB 未连接,搜索记录与种子缓存不会被保存(其余功能可用)。在 .env 配置正确的 MONGODB_URI(云端实例请填真实密码)后刷新即可:': 'MongoDB not connected; search history and torrent cache will not be saved (other features still work). Set a valid MONGODB_URI in .env (use the real password for cloud instances) and refresh:' } export const en2zh: Record = Object.fromEntries( diff --git a/server/api/hot.get.ts b/server/api/hot.get.ts new file mode 100644 index 0000000..969f407 --- /dev/null +++ b/server/api/hot.get.ts @@ -0,0 +1,21 @@ +import { searchesCol, tryMongo } from '../utils/mongo' + +/** 热搜关键词:按搜索次数降序,返回前 N 个 */ +export default defineEventHandler(async (event) => { + const limit = Math.min(Number(getQuery(event).limit) || 10, 50) + + const data = await tryMongo(async () => { + const col = await searchesCol() + const hot = await col + .aggregate<{ _id: string; count: number; last: Date }>([ + { $group: { _id: '$keyword', count: { $sum: 1 }, last: { $max: '$createdAt' } } }, + { $sort: { count: -1, last: -1 } }, + { $limit: limit } + ]) + .toArray() + return hot.map((h) => ({ keyword: h._id, count: h.count, last: h.last })) + }) + + if (!data) return { keywords: [], offline: true } + return { keywords: data, offline: false } +}) diff --git a/server/utils/mongo.ts b/server/utils/mongo.ts index d2924a2..9b24993 100644 --- a/server/utils/mongo.ts +++ b/server/utils/mongo.ts @@ -15,8 +15,10 @@ export async function getClient(): Promise { if (client) return client if (!connecting) { const next = new MongoClient(mongoUri(), { - serverSelectionTimeoutMS: 3000, - connectTimeoutMS: 3000 + serverSelectionTimeoutMS: 10000, + connectTimeoutMS: 20000, + socketTimeoutMS: 30000, + retryWrites: true }) connecting = next .connect()