-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Expand file tree
/
Copy pathindex.ts
More file actions
99 lines (93 loc) · 3.17 KB
/
Copy pathindex.ts
File metadata and controls
99 lines (93 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import type { SourceID, SourceResponse } from "@shared/types"
import { getters } from "#/getters"
import { getCacheTable } from "#/database/cache"
import type { CacheInfo } from "#/types"
export default defineEventHandler(async (event): Promise<SourceResponse> => {
try {
const query = getQuery(event)
const latest = query.latest !== undefined && query.latest !== "false"
let id = query.id as SourceID
const isValid = (id: SourceID) => !id || !sources[id] || !getters[id]
if (isValid(id)) {
const redirectID = sources?.[id]?.redirect
if (redirectID) id = redirectID
if (isValid(id)) throw new Error("Invalid source id")
}
const cacheTable = await getCacheTable()
// Date.now() in Cloudflare Worker will not update throughout the entire runtime.
const now = Date.now()
let cache: CacheInfo | undefined
if (cacheTable) {
cache = await cacheTable.get(id)
if (cache) {
// if (cache) {
// interval 刷新间隔,对于缓存失效也要执行的。本质上表示本来内容更新就很慢,这个间隔内可能内容压根不会更新。
// 默认 10 分钟,是低于 TTL 的,但部分 Source 的更新间隔会超过 TTL,甚至有的一天更新一次。
if (now - cache.updated < sources[id].interval) {
return {
status: "success",
id,
updatedTime: now,
items: cache.items,
}
}
// 而 TTL 缓存失效时间,在时间范围内,就算内容更新了也要用这个缓存。
// 复用缓存是不会更新时间的。
if (now - cache.updated < TTL) {
// 有 latest
// 没有 latest,但服务器禁止登录
// 没有 latest
// 有 latest,服务器可以登录但没有登录
if (!latest || (!event.context.disabledLogin && !event.context.user)) {
return {
status: "cache",
id,
updatedTime: cache.updated,
items: cache.items,
}
}
}
}
}
try {
const newData = (await getters[id]()).slice(0, 30)
if (cacheTable && newData.length) {
if (event.context.waitUntil) event.context.waitUntil(cacheTable.set(id, newData))
else await cacheTable.set(id, newData)
}
logger.success(`fetch ${id} latest`)
return {
status: "success",
id,
updatedTime: now,
items: newData,
}
} catch (e) {
if (cache!) {
return {
status: "cache",
id,
updatedTime: cache.updated,
items: cache.items,
}
} else {
// 单个 source 抓取失败时(例如目标站点封禁/405/超时)不要让整个接口 500,
// 以免 MCP/Agent 调用直接崩溃。返回空列表并记录日志。
logger.error(e)
return {
status: "success",
id,
updatedTime: now,
items: [],
}
}
}
}
} catch (e: any) {
logger.error(e)
throw createError({
statusCode: 500,
message: e instanceof Error ? e.message : "Internal Server Error",
})
}
})