拉取项目
git clone https://github.com/imsyy/DailyHotApi.git
cd DailyHotApi
安装依赖
npm install
修改配置
复制 /.env.example
文件并重命名为 /.env
并修改配置,包括Redis
端口密码等
...
# 允许的域名
ALLOWED_DOMAIN = "*"
# 允许的主域名,填写格式为 imsyy.top
## 若填写该项,将忽略 ALLOWED_DOMAIN
ALLOWED_HOST="jianbing.tk"
# ROBOT
DISALLOW_ROBOT = true
# Redis
REDIS_HOST="127.0.0.1"
REDIS_PORT=6379
REDIS_PASSWORD="Your Password"
...
开发
npm run dev
成功启动后程序会在控制台输出可访问的地址
编译运行
npm run build
npm run start
成功启动后程序会在控制台输出可访问的地址
自定义RSS订阅
自定义RSS订阅通常只要新增两个部分的内容:
1.src\routes
路径下的xxx.ts
文件,比如nodeseek.ts
,用来处理与 NodeSeek
相关的路由和数据获取逻辑。
2.src\router.type.d.ts
文件中的特定数据结构的类型接口。例如,"36kr"
类型接口,在36kr.ts
中使用:
return {
...result,
data: list.map((v: RouterType["36kr"]) => { //此处的RouterType
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
处理:
/* parseRSS.ts */
import RSSParser from "rss-parser";
import logger from "./logger.js";
/**
* 提取 RSS 内容
* @param content HTML 内容
* @returns RSS 内容
*/
export const extractRss = (content: string): string | null => {
// 匹配 <rss> 标签及内容
const rssRegex = /(<rss[\s\S]*?<\/rss>)/i;
const matches = content.match(rssRegex);
return matches ? matches[0] : null;
};
/**
* 解析 RSS 内容
* @param rssContent RSS 内容
* @returns 解析后的 RSS 内容
*/
export const parseRSS = async (rssContent: string) => {
const parser = new RSSParser();
// 是否为网址
const isUrl = (url: string) => {
try {
new URL(url);
return true;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} 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
文件正确解析:
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
:
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)) // 过滤掉 id 在 filteredIds 列表中的条目
.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部署
将本地开发好的项目部署至自己的服务器
# 列出所有正在运行的容器
docker ps
# 列出所有镜像,并筛选出名称包含 dailyhot-api 的镜像
docker images | grep dailyhot-api
# 停止名为 dailyhot-api 的容器(可以使用容器名称或 ID)
docker stop dailyhot-api
# 删除名为 dailyhot-api 的容器(可以使用容器名称或 ID)
docker rm dailyhot-api
# 构建镜像,使用当前目录的 Dockerfile,并将镜像命名为 dailyhot-api
docker build -t dailyhot-api .
# 运行容器,设置自动重启,映射主机的 6688 端口到容器的 6688 端口,并在后台运行
docker run --restart always -p 6688:6688 -d --name dailyhot-api dailyhot-api
# 或使用 Docker Compose 启动服务,后台运行
docker-compose up -d
注意:原项目中包含Redis
缓存配置,需与服务器中端口和密码保持一致。
此外,Redis
缓存时长默认为 3600 秒(即一小时),你可以在 config.ts
文件中通过修改环境变量 CACHE_TTL
的值来调整该时长。
成品展示
预览:神马值得看