mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +08:00
chore(server): clean up
This commit is contained in:
parent
f8303382ef
commit
63db52fb9a
@ -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))
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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[]>
|
||||
|
@ -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,
|
||||
|
@ -15,7 +15,7 @@ const originMetadata: Metadata = {
|
||||
},
|
||||
tech: {
|
||||
name: "科技",
|
||||
sources: ["ithome", "v2ex", "coolapk", "36kr-quick"],
|
||||
sources: ["ithome", "v2ex", "coolapk"],
|
||||
},
|
||||
finance: {
|
||||
name: "财经",
|
||||
|
@ -16,6 +16,11 @@ export const originSources = {
|
||||
name: "V2EX",
|
||||
color: "slate",
|
||||
home: "https://v2ex.com/",
|
||||
sub: {
|
||||
share: {
|
||||
title: "最新分享",
|
||||
},
|
||||
},
|
||||
},
|
||||
"zhihu": {
|
||||
name: "知乎",
|
||||
|
@ -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])
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user