chore(server): clean up

This commit is contained in:
Ou 2024-10-20 20:28:49 +08:00
parent f8303382ef
commit 63db52fb9a
9 changed files with 80 additions and 62 deletions

View File

@ -2,7 +2,7 @@ import process from "node:process"
import { TTL } from "@shared/consts"
import type { SourceID, SourceResponse } from "@shared/types"
import { sources } from "@shared/sources"
import { sourcesFn } from "#/sources"
import { sourcesGetters } from "#/sources"
import { useCache } from "#/hooks/useCache"
export default defineEventHandler(async (event): Promise<SourceResponse> => {
@ -10,7 +10,7 @@ export default defineEventHandler(async (event): Promise<SourceResponse> => {
let id = getRouterParam(event, "id") as SourceID
const query = getQuery(event)
const latest = query.latest !== undefined && query.latest !== "false"
const isValid = (id: SourceID) => !id || !sources[id] || !sourcesFn[id]
const isValid = (id: SourceID) => !id || !sources[id] || !sourcesGetters[id]
if (isValid(id)) {
const redirectID = sources?.[id].redirect
@ -54,7 +54,7 @@ export default defineEventHandler(async (event): Promise<SourceResponse> => {
}
}
const data = await sourcesFn[id]()
const data = (await sourcesGetters[id]()).slice(0, 30)
logger.success(`fetch ${id} latest`)
if (cacheTable) {
if (event.context.waitUntil) event.context.waitUntil(cacheTable.set(id, data))

View File

@ -1,7 +1,7 @@
import type { NewsItem } from "@shared/types"
import { load } from "cheerio"
export default defineSource(async () => {
const quick = defineSource(async () => {
const url = "https://www.36kr.com/newsflashes"
const response = await $fetch(url) as any
const $ = load(response)
@ -25,5 +25,10 @@ export default defineSource(async () => {
}
})
return news.slice(0, 30)
return news
})
export default defineSource({
"36kr": quick,
"36kr-quick": quick,
})

View File

@ -1,4 +1,4 @@
import type { NewsItem, SourceID } from "@shared/types"
import type { SourceID } from "@shared/types"
import weibo from "./weibo"
import zaobao from "./zaobao"
import v2ex from "./v2ex"
@ -11,11 +11,12 @@ import kr36 from "./36kr"
import wallstreetcn from "./wallstreetcn"
import douyin from "./douyin"
import toutiao from "./toutiao"
import type { SourceGetter } from "#/types"
export const sourcesFn = {
export const sourcesGetters = {
weibo,
zaobao,
v2ex,
...v2ex,
ithome,
zhihu,
coolapk,
@ -24,5 +25,5 @@ export const sourcesFn = {
wallstreetcn,
douyin,
toutiao,
"36kr-quick": kr36,
} as Record<SourceID, () => Promise<NewsItem[]>>
...kr36,
} as Record<SourceID, SourceGetter>

View File

@ -16,7 +16,7 @@ interface Res {
}[]
}
export default defineSource(async () => {
const share = defineSource(async () => {
const res = await Promise.all(["create", "ideas", "programmer", "share"].map(k => $fetch(`https://www.v2ex.com/feed/${k}.json`) as Promise< Res>))
if (!res?.[0]?.items?.length) throw new Error("Cannot fetch data")
return res.map(k => k.items).flat().map(k => ({
@ -26,5 +26,10 @@ export default defineSource(async () => {
date: k.date_modified ?? k.date_published,
},
url: k.url,
})).sort((m, n) => m.extra.date < n.extra.date ? 1 : -1).slice(0, 30)
})).sort((m, n) => m.extra.date < n.extra.date ? 1 : -1)
})
export default defineSource({
"v2ex": share,
"v2ex-share": share,
})

View File

@ -44,3 +44,34 @@ export interface UserInfo {
created: number
updated: number
}
export interface RSSHubOption {
// default: true
sorted?: boolean
// default: 20
limit?: number
}
export interface SourceOption {
// default: false
hiddenDate?: boolean
}
export interface FallbackResponse {
code: number
message: string
name: string
title: string
subtitle: string
total: number
updateTime: string
data: {
title: string
desc: string
time?: string
url: string
mobileUrl: string
}[]
}
export type SourceGetter = () => Promise<NewsItem[]>

View File

@ -1,36 +1,18 @@
import type { NewsItem, RSSHubInfo, SourceID } from "@shared/types"
import type { SourceID } from "@shared/types"
import defu from "defu"
import type { FallbackResponse, RSSHubOption, RSSHubInfo as RSSHubResponse, SourceGetter, SourceOption } from "#/types"
export function defineSource(source: () => Promise<NewsItem[]>): () => Promise<NewsItem[]> {
type X = SourceGetter | Partial<Record<SourceID, SourceGetter>>
export function defineSource<T extends X>(source: T): T {
return source
}
interface SourceOption {
// default: false
hiddenDate?: boolean
}
interface FallbackRes {
code: number
message: string
name: string
title: string
subtitle: string
total: number
updateTime: string
data: {
title: string
desc: string
time?: string
url: string
mobileUrl: string
}[]
}
export function defineFallbackSource(id: SourceID, option?: SourceOption): () => Promise<NewsItem[]> {
export function defineFallbackSource(id: SourceID, option?: SourceOption): SourceGetter {
return async () => {
const url = `https://smzdk.top/api/${id}/new`
const res: FallbackRes = await $fetch(url)
const res: FallbackResponse = await $fetch(url)
if (res.code !== 200 || !res.data) throw new Error(res.message)
return res.data.slice(0, 30).map(item => ({
return res.data.map(item => ({
extra: {
date: !option?.hiddenDate && item.time,
},
@ -42,11 +24,11 @@ export function defineFallbackSource(id: SourceID, option?: SourceOption): () =>
}
}
export function defineRSSSource(url: string, option?: SourceOption): () => Promise<NewsItem[]> {
export function defineRSSSource(url: string, option?: SourceOption): SourceGetter {
return async () => {
const data = await rss2json(url)
if (!data?.items.length) throw new Error("Cannot fetch data")
return data.items.slice(0, 30).map(item => ({
return data.items.map(item => ({
title: item.title,
url: item.link,
id: item.link,
@ -57,28 +39,21 @@ export function defineRSSSource(url: string, option?: SourceOption): () => Promi
}
}
interface RSSHubOption {
// default: true
sorted?: boolean
// default: 20
limit?: number
}
export function defineRSSHubSource(route: string, RSSHubOptions?: RSSHubOption, sourceOption?: SourceOption): () => Promise<NewsItem[]> {
export function defineRSSHubSource(route: string, RSSHubOptions?: RSSHubOption, sourceOption?: SourceOption): SourceGetter {
return async () => {
// "https://rsshub.pseudoyu.com"
const RSSHubBase = "https://rsshub.rssforever.com"
const url = new URL(route, RSSHubBase)
url.searchParams.set("format", "json")
const defaultRSSHubOption: RSSHubOption = {
RSSHubOptions = defu<RSSHubOption, RSSHubOption[]>(RSSHubOptions, {
sorted: true,
limit: 20,
}
Object.assign(defaultRSSHubOption, RSSHubOptions)
Object.entries(defaultRSSHubOption).forEach(([key, value]) => {
})
Object.entries(RSSHubOptions).forEach(([key, value]) => {
url.searchParams.set(key, value.toString())
})
const data: RSSHubInfo = await $fetch(url)
return data.items.slice(0, 30).map(item => ({
const data: RSSHubResponse = await $fetch(url)
return data.items.map(item => ({
title: item.title,
url: item.url,
id: item.id ?? item.url,

View File

@ -15,7 +15,7 @@ const originMetadata: Metadata = {
},
tech: {
name: "科技",
sources: ["ithome", "v2ex", "coolapk", "36kr-quick"],
sources: ["ithome", "v2ex", "coolapk"],
},
finance: {
name: "财经",

View File

@ -16,6 +16,11 @@ export const originSources = {
name: "V2EX",
color: "slate",
home: "https://v2ex.com/",
sub: {
share: {
title: "最新分享",
},
},
},
"zhihu": {
name: "知乎",

View File

@ -4,25 +4,21 @@ import { useRegisterSW } from "virtual:pwa-register/react"
export function usePWA() {
const {
offlineReady: [offlineReady, setOfflineReady],
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW()
useEffect(() => {
if (offlineReady) {
toast.info("PWA 准备好了")
} else if (needRefresh) {
toast("有更新,点击更新", {
if (needRefresh) {
toast("网站有更新,点击更新", {
action: {
label: "更新",
onClick: () => updateServiceWorker(true),
},
onDismiss: () => {
setOfflineReady(false)
setNeedRefresh(false)
},
})
}
}, [offlineReady, needRefresh, updateServiceWorker, setOfflineReady, setNeedRefresh])
}, [needRefresh, updateServiceWorker, setNeedRefresh])
}