Skip to content

Commit afa85c6

Browse files
authored
feat(route): 添加BestBlogs.dev文章路由 (#16659)
* Add BestBlogs.dev * fix per review comments
1 parent d354f81 commit afa85c6

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

lib/routes/bestblogs/feeds.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { Route } from '@/types';
2+
import ofetch from '@/utils/ofetch';
3+
import { parseDate } from '@/utils/parse-date';
4+
5+
export const route: Route = {
6+
path: '/feeds/:category?',
7+
categories: ['programming'],
8+
example: '/bestblogs/feeds/featured',
9+
parameters: { category: 'the category of articles. Can be `programming`, `ai`, `product`, `business` or `featured`. Default is `featured`' },
10+
features: {
11+
requireConfig: false,
12+
requirePuppeteer: false,
13+
antiCrawler: false,
14+
supportBT: false,
15+
supportPodcast: false,
16+
supportScihub: false,
17+
},
18+
name: '文章列表',
19+
maintainers: ['zhenlohuang'],
20+
handler,
21+
};
22+
23+
class APIRequest {
24+
keyword?: string;
25+
qualifiedFilter: string;
26+
sourceId?: string;
27+
category?: string;
28+
timeFilter: string;
29+
language: string;
30+
userLanguage: string;
31+
sortType: string;
32+
currentPage: number;
33+
pageSize: number;
34+
35+
constructor({ keyword = '', qualifiedFilter = 'true', sourceId = '', category = '', timeFilter = '1w', language = 'all', userLanguage = 'zh', sortType = 'default', currentPage = 1, pageSize = 10 } = {}) {
36+
this.keyword = keyword;
37+
this.qualifiedFilter = qualifiedFilter;
38+
this.sourceId = sourceId;
39+
this.category = category;
40+
this.timeFilter = timeFilter;
41+
this.language = language;
42+
this.userLanguage = userLanguage;
43+
this.sortType = sortType;
44+
this.currentPage = currentPage;
45+
this.pageSize = pageSize;
46+
}
47+
48+
toJson(): string {
49+
const requestBody = {
50+
keyword: this.keyword,
51+
qualifiedFilter: this.qualifiedFilter,
52+
sourceId: this.sourceId,
53+
category: this.category,
54+
timeFilter: this.timeFilter,
55+
language: this.language,
56+
userLanguage: this.userLanguage,
57+
sortType: this.sortType,
58+
currentPage: this.currentPage,
59+
pageSize: this.pageSize,
60+
};
61+
62+
return JSON.stringify(requestBody);
63+
}
64+
}
65+
66+
async function handler(ctx) {
67+
const defaultPageSize = 100;
68+
const defaultTimeFilter = '1w';
69+
const { category = 'featured' } = ctx.req.param();
70+
71+
const apiRequest = new APIRequest({
72+
category,
73+
pageSize: defaultPageSize,
74+
qualifiedFilter: category === 'featured' ? 'true' : 'false',
75+
timeFilter: defaultTimeFilter,
76+
});
77+
78+
const apiUrl = 'https://api.bestblogs.dev/api/resource/list';
79+
const response = await ofetch(apiUrl, {
80+
headers: {
81+
'Content-Type': 'application/json',
82+
},
83+
method: 'POST',
84+
body: apiRequest.toJson(),
85+
});
86+
87+
if (!response || !response.data || !response.data.dataList) {
88+
throw new Error('Invalid API response: ' + JSON.stringify(response));
89+
}
90+
91+
const articles = response.data.dataList;
92+
93+
const items = articles.map((article) => ({
94+
title: article.title,
95+
link: article.url,
96+
description: article.summary,
97+
pubDate: parseDate(article.publishDateTimeStr),
98+
author: Array.isArray(article.authors) ? article.authors.map((author) => ({ name: author })) : [{ name: article.authors }],
99+
category: article.category,
100+
}));
101+
102+
return {
103+
title: `Bestblogs.dev`,
104+
link: `https://www.bestblogs.dev/feeds`,
105+
item: items,
106+
};
107+
}

lib/routes/bestblogs/namespace.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { Namespace } from '@/types';
2+
3+
export const namespace: Namespace = {
4+
name: 'bestblogs.dev',
5+
url: 'www.bestblogs.dev',
6+
};

0 commit comments

Comments
 (0)