今日热榜 API,一个聚合热门数据的 API 接口,支持 RSS 模式 及 Vercel 部署,并含有配套的前端项目。
拉取项目
1 2
| git clone https://github.com/imsyy/DailyHotApi.git cd DailyHotApi
|
安装依赖
修改配置
复制 /.env.example 文件并重命名为 /.env 并修改配置,包括Redis端口密码等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ...
ALLOWED_DOMAIN = "*"
ALLOWED_HOST="jianbing.tk"
DISALLOW_ROBOT = true
REDIS_HOST="127.0.0.1" REDIS_PORT=6379 REDIS_PASSWORD="Your Password" ...
|
开发
成功启动后程序会在控制台输出可访问的地址
编译运行
1 2
| npm run build npm run start
|
成功启动后程序会在控制台输出可访问的地址
自定义RSS订阅通常只要新增两个部分的内容:
1.src\routes路径下的xxx.ts文件,比如nodeseek.ts,用来处理与 NodeSeek相关的路由和数据获取逻辑。
2.src\router.type.d.ts文件中的特定数据结构的类型接口。例如,"36kr" 类型接口,在36kr.ts中使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| return { ...result, data: list.map((v: RouterType["36kr"]) => { const item = v.templateMaterial; return { id: v.itemId, title: item.widgetTitle, cover: item.widgetImage, author: item.authorName, timestamp: getTime(v.publishTime), hot: item.statCollect || undefined, url: `https://www.36kr.com/p/${v.itemId}`, mobileUrl: `https://m.36kr.com/p/${v.itemId}`, }; }), };
|
当然也可以不定义新的类型接口,只添加xxx.ts文件,借助parseRSS处理:
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
| import RSSParser from "rss-parser"; import logger from "./logger.js";
export const extractRss = (content: string): string | null => { const rssRegex = /(<rss[\s\S]*?<\/rss>)/i; const matches = content.match(rssRegex); return matches ? matches[0] : null; };
export const parseRSS = async (rssContent: string) => { const parser = new RSSParser(); const isUrl = (url: string) => { try { new URL(url); return true; } catch (error) { return false; } }; try { const feed = isUrl(rssContent) ? await parser.parseURL(rssContent) : await parser.parseString(rssContent); const items = feed.items.map((item) => ({ title: item.title, link: item.link, pubDate: item.pubDate, author: item.creator ?? item.author, content: item.content, contentSnippet: item.contentSnippet, guid: item.guid, categories: item.categories, })); return items; } catch (error) { logger.error("❌ [RSS] An error occurred while parsing RSS content"); throw error; } };
|
一般网站的RSS订阅都能借助src\utils\parseRSS.ts文件正确解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const getList = async (noCache: boolean) => { const url = `https://rss.nodeseek.com/`; const result = await get({ url, noCache }); const list = await parseRSS(result.data); return { ...result, data: list.map((v, i) => ({ id: v.guid || i, title: v.title || "", desc: v.content?.trim() || "", author: v.author, timestamp: getTime(v.pubDate || 0), hot: undefined, url: v.link || "", mobileUrl: v.link || "", })), }; };
|
部分订阅需要自己处理请求返回的数据,同时新增RouterType,且不借助parseRSS处理result.data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const getList = async (options: Options, noCache: boolean) => { const { type } = options; const url = `https://linux.do/${type}.json`; const result = await get({ url, noCache }); const list = result.data?.topic_list?.topics; const filteredIds = [5, 293017, 298988]; return { fromCache: result.fromCache, updateTime: result.updateTime, data: list .filter((v: RouterType["linuxdo"]) => !filteredIds.includes(v.id)) .map((v: RouterType["linuxdo"]) => ({ id: v.id, title: v.title, timestamp: v.last_comment_at || v.created_at, hot: v.highest_post_number, url: `https://linux.do/t/topic/${v.id}`, mobileUrl: `https://linux.do/t/topic/${v.id}`, })), }; };
|
注意:除了自定义RSS订阅以外,还可以自行修改DailyHotApi项目的icon、页脚等信息,其中Home.tsx中的<img>标签中的图片为base64格式,需自行转码。
Docker部署
将本地开发好的项目部署至自己的服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| docker ps
docker images | grep dailyhot-api
docker stop dailyhot-api
docker rm dailyhot-api
docker build -t dailyhot-api .
docker run --restart always -p 6688:6688 -d --name dailyhot-api dailyhot-api
docker-compose up -d
|
注意:原项目中包含Redis缓存配置,需与服务器中端口和密码保持一致。
此外,Redis 缓存时长默认为 3600 秒(即一小时),你可以在 config.ts 文件中通过修改环境变量 CACHE_TTL 的值来调整该时长。
成品展示

预览:神马值得看
参考