From 63db52fb9afc476f5d3dba5fdc32efc139a250f1 Mon Sep 17 00:00:00 2001 From: Ou Date: Sun, 20 Oct 2024 20:28:49 +0800 Subject: [PATCH] chore(server): clean up --- server/api/s/[id].ts | 6 ++--- server/sources/36kr.ts | 9 +++++-- server/sources/index.ts | 11 ++++---- server/sources/v2ex.ts | 9 +++++-- server/types.ts | 31 ++++++++++++++++++++++ server/utils/source.ts | 59 ++++++++++++----------------------------- shared/metadata.ts | 2 +- shared/sources.ts | 5 ++++ src/hooks/usePWA.ts | 10 +++---- 9 files changed, 80 insertions(+), 62 deletions(-) diff --git a/server/api/s/[id].ts b/server/api/s/[id].ts index 84156f9..da01a06 100644 --- a/server/api/s/[id].ts +++ b/server/api/s/[id].ts @@ -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 => { @@ -10,7 +10,7 @@ export default defineEventHandler(async (event): Promise => { 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 => { } } - 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)) diff --git a/server/sources/36kr.ts b/server/sources/36kr.ts index c72091e..9c071b4 100644 --- a/server/sources/36kr.ts +++ b/server/sources/36kr.ts @@ -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, }) diff --git a/server/sources/index.ts b/server/sources/index.ts index 66e5968..ac0d4b6 100644 --- a/server/sources/index.ts +++ b/server/sources/index.ts @@ -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 Promise> + ...kr36, +} as Record diff --git a/server/sources/v2ex.ts b/server/sources/v2ex.ts index 015dca9..cc3dd7e 100644 --- a/server/sources/v2ex.ts +++ b/server/sources/v2ex.ts @@ -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, }) diff --git a/server/types.ts b/server/types.ts index 046490a..d233e11 100644 --- a/server/types.ts +++ b/server/types.ts @@ -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 diff --git a/server/utils/source.ts b/server/utils/source.ts index b1833e4..99a6fa7 100644 --- a/server/utils/source.ts +++ b/server/utils/source.ts @@ -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): () => Promise { +type X = SourceGetter | Partial> +export function defineSource(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 { +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 { +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 { +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(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, diff --git a/shared/metadata.ts b/shared/metadata.ts index 080d0d9..d667ab1 100644 --- a/shared/metadata.ts +++ b/shared/metadata.ts @@ -15,7 +15,7 @@ const originMetadata: Metadata = { }, tech: { name: "科技", - sources: ["ithome", "v2ex", "coolapk", "36kr-quick"], + sources: ["ithome", "v2ex", "coolapk"], }, finance: { name: "财经", diff --git a/shared/sources.ts b/shared/sources.ts index 6faaed0..2064f4f 100644 --- a/shared/sources.ts +++ b/shared/sources.ts @@ -16,6 +16,11 @@ export const originSources = { name: "V2EX", color: "slate", home: "https://v2ex.com/", + sub: { + share: { + title: "最新分享", + }, + }, }, "zhihu": { name: "知乎", diff --git a/src/hooks/usePWA.ts b/src/hooks/usePWA.ts index 2063b72..4e9d04d 100644 --- a/src/hooks/usePWA.ts +++ b/src/hooks/usePWA.ts @@ -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]) }