From 200f0881a5d3cc10885efb6efa02979166a05b12 Mon Sep 17 00:00:00 2001
From: cat_shark <1716967236@qq.com>
Date: Sat, 4 Jul 2026 22:34:07 +0800
Subject: [PATCH] =?UTF-8?q?feat(lpt-ai)=EF=BC=9A=E6=96=B0=E5=A2=9E=20/fetc?=
=?UTF-8?q?h-title=20=E7=BD=91=E9=A1=B5=E6=A0=87=E9=A2=98=E6=8A=93?=
=?UTF-8?q?=E5=8F=96=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
POST /fetch-title { url } → { url, title }
- 服务端 fetch 目标页面,正则提取
标签
- 10 秒超时,抓取失败时返回 title: null
- 请求日志记录到 logStore
---
src/routes/ai.ts | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/src/routes/ai.ts b/src/routes/ai.ts
index 5ca68b9..bbef803 100644
--- a/src/routes/ai.ts
+++ b/src/routes/ai.ts
@@ -5,6 +5,7 @@
import type { FastifyInstance } from "fastify";
import { isAvailable, loadConfig } from "../llm/client.js";
import { createTask, getTask, type TaskType } from "../task-queue.js";
+import { logStore } from "../admin/store.js";
export async function aiRoutes(app: FastifyInstance): Promise {
const config = loadConfig();
@@ -39,4 +40,48 @@ export async function aiRoutes(app: FastifyInstance): Promise {
}
return task;
});
+
+ /** 抓取网页标题 */
+ app.post<{ Body: { url: string } }>("/fetch-title", async (request, reply) => {
+ const { url } = request.body ?? {};
+ const t0 = Date.now();
+
+ if (!url || typeof url !== "string" || (!url.startsWith("http://") && !url.startsWith("https://"))) {
+ return reply.status(400).send({ error: "无效的 URL" });
+ }
+
+ try {
+ const controller = new AbortController();
+ const timer = setTimeout(() => controller.abort(), 10_000);
+ const response = await fetch(url, { signal: controller.signal });
+ clearTimeout(timer);
+
+ const html = await response.text();
+ const match = html.match(/]*>([^<]+)<\/title>/i);
+ const title = match ? match[1].trim() : null;
+
+ logStore.add({
+ timestamp: new Date().toISOString(),
+ endpoint: "/fetch-title",
+ model: "",
+ durationMs: Date.now() - t0,
+ status: 200,
+ requestBody: { url },
+ responseBody: { url, title },
+ });
+ return { url, title };
+ } catch (err) {
+ logStore.add({
+ timestamp: new Date().toISOString(),
+ endpoint: "/fetch-title",
+ model: "",
+ durationMs: Date.now() - t0,
+ status: 502,
+ requestBody: { url },
+ responseBody: { url, title: null },
+ error: err instanceof Error ? err.message : String(err),
+ });
+ return { url, title: null };
+ }
+ });
}