Skip to content

Commit 4c4cbba

Browse files
authored
feat(route): add codefather (编程导航) routes (#20922)
* feat(route): add LinuxDo RSS routes - Add /linux-do/posts route for latest posts - Add /linux-do/top/:period route for trending topics with period filter - Optimize /linux-do/latest route - Update namespace documentation with descriptions - Handle Zstandard compression by using Accept-Encoding: identity * revert: remove linux-do routes (PR rejected) * feat(route): add codefather (编程导航) routes - Add posts route with hot/new/recommend sorting and category filter - Add questions route with new/hot sorting - Support for 编程导航 programming community * fix: address all PR review comments - Reorder path params: category before sort - Remove config.trueUA (not needed) - Use upvotes/comments fields instead of description - Remove title slicing - Change default sort to 'new' - Remove title from description - Remove content truncation
1 parent e9e8cb0 commit 4c4cbba

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed

lib/routes/codefather/namespace.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { Namespace } from '@/types';
2+
3+
export const namespace: Namespace = {
4+
name: '编程导航',
5+
url: 'www.codefather.cn',
6+
description: '编程导航是一个编程学习交流社区,由程序员鱼皮创建,提供学习路线、项目教程、求职攻略、技术交流等内容。',
7+
};

lib/routes/codefather/posts.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import type { Route } from '@/types';
2+
import ofetch from '@/utils/ofetch';
3+
import { parseDate } from '@/utils/parse-date';
4+
5+
const validCategories = new Set(['交流', '学习', '项目', '资源', '经验']);
6+
7+
const sortFieldMap: Record<string, { field: string; name: string }> = {
8+
hot: { field: 'thumbNum', name: '热门' },
9+
new: { field: 'createTime', name: '最新' },
10+
recommend: { field: 'recommendTime', name: '推荐' },
11+
};
12+
13+
export const route: Route = {
14+
path: '/posts/:category?/:sort?',
15+
categories: ['programming'],
16+
example: '/codefather/posts',
17+
parameters: {
18+
category: '分类,可选 `交流`、`学习`、`项目`、`资源`、`经验`,默认为全部',
19+
sort: '排序方式,可选 `new`(最新)、`hot`(热门)、`recommend`(推荐),默认为 `new`',
20+
},
21+
features: {
22+
requireConfig: false,
23+
requirePuppeteer: false,
24+
antiCrawler: false,
25+
supportBT: false,
26+
supportPodcast: false,
27+
supportScihub: false,
28+
},
29+
radar: [
30+
{
31+
source: ['www.codefather.cn/', 'www.codefather.cn'],
32+
target: '/posts',
33+
},
34+
],
35+
name: '帖子',
36+
maintainers: ['JackyST0'],
37+
handler,
38+
description: '获取编程导航社区的帖子,支持按热门、最新、推荐排序,支持按分类筛选。',
39+
};
40+
41+
async function handler(ctx) {
42+
const category = ctx.req.param('category');
43+
const sort = ctx.req.param('sort') || 'new';
44+
45+
const sortConfig = sortFieldMap[sort] || sortFieldMap.new;
46+
47+
const requestBody: Record<string, unknown> = {
48+
current: 1,
49+
pageSize: 20,
50+
sortField: sortConfig.field,
51+
sortOrder: 'descend',
52+
};
53+
54+
if (category && validCategories.has(category)) {
55+
requestBody.category = category;
56+
}
57+
58+
const response = await ofetch('https://api.codefather.cn/api/post/list/page/vo', {
59+
method: 'POST',
60+
headers: {
61+
'Content-Type': 'application/json',
62+
},
63+
body: requestBody,
64+
});
65+
66+
if (response.code !== 0) {
67+
throw new Error(`API error: ${response.message}`);
68+
}
69+
70+
const records = response.data?.records || [];
71+
72+
const items = records.map((item: Record<string, unknown>) => {
73+
const content = (item.content as string) || '';
74+
const pictureList = (item.pictureList as string[]) || [];
75+
const user = (item.user as Record<string, unknown>) || {};
76+
const tags = (item.tags as Array<{ tagName: string }>) || [];
77+
78+
// Build description content
79+
let description = `<p>${content.replaceAll('\n', '<br>')}</p>`;
80+
81+
// Add images
82+
if (pictureList.length > 0) {
83+
description += '<div>';
84+
for (const pic of pictureList) {
85+
description += `<img src="${pic}" style="max-width: 100%;" />`;
86+
}
87+
description += '</div>';
88+
}
89+
90+
return {
91+
title: content.split('\n')[0] || '无标题',
92+
link: `https://www.codefather.cn/post/${item.id}`,
93+
description,
94+
pubDate: parseDate(item.createTime as number),
95+
author: user.userName as string,
96+
category: [item.category as string, ...tags.map((t) => t.tagName)].filter(Boolean),
97+
upvotes: item.thumbNum as number,
98+
comments: item.commentNum as number,
99+
};
100+
});
101+
102+
const categoryName = category ? `${category} - ` : '';
103+
const sortName = sortConfig.name;
104+
105+
return {
106+
title: `编程导航 - ${categoryName}${sortName}帖子`,
107+
link: 'https://www.codefather.cn/',
108+
description: `编程导航社区${categoryName}${sortName}帖子`,
109+
item: items,
110+
};
111+
}

lib/routes/codefather/questions.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import type { Route } from '@/types';
2+
import ofetch from '@/utils/ofetch';
3+
import { parseDate } from '@/utils/parse-date';
4+
5+
export const route: Route = {
6+
path: '/questions/:sort?',
7+
categories: ['programming'],
8+
example: '/codefather/questions',
9+
parameters: {
10+
sort: '排序方式,可选 `new`(最新)、`hot`(热门),默认为 `new`',
11+
},
12+
features: {
13+
requireConfig: false,
14+
requirePuppeteer: false,
15+
antiCrawler: false,
16+
supportBT: false,
17+
supportPodcast: false,
18+
supportScihub: false,
19+
},
20+
radar: [
21+
{
22+
source: ['www.codefather.cn/qa', 'www.codefather.cn'],
23+
target: '/questions',
24+
},
25+
],
26+
name: '问答',
27+
maintainers: ['JackyST0'],
28+
handler,
29+
description: '获取编程导航社区的问答内容,支持按最新、热门排序。',
30+
};
31+
32+
async function handler(ctx) {
33+
const sort = ctx.req.param('sort') || 'new';
34+
35+
const sortConfig = sort === 'hot' ? { field: 'favourNum', name: '热门' } : { field: 'createTime', name: '最新' };
36+
37+
const response = await ofetch('https://api.codefather.cn/api/qa/list/page/vo', {
38+
method: 'POST',
39+
headers: {
40+
'Content-Type': 'application/json',
41+
},
42+
body: {
43+
current: 1,
44+
pageSize: 20,
45+
sortField: sortConfig.field,
46+
sortOrder: 'descend',
47+
},
48+
});
49+
50+
if (response.code !== 0) {
51+
throw new Error(`API error: ${response.message}`);
52+
}
53+
54+
const records = response.data?.records || [];
55+
56+
const items = records.map((item: Record<string, unknown>) => {
57+
const title = (item.title as string) || '无标题';
58+
const content = (item.content as string) || '';
59+
const user = (item.user as Record<string, unknown>) || {};
60+
const tags = (item.tags as string[]) || [];
61+
const bestComment = item.bestComment as Record<string, unknown> | undefined;
62+
63+
// Build description content
64+
let description = `<div>${content.replaceAll('\n', '<br>')}</div>`;
65+
66+
// Add best answer
67+
if (bestComment) {
68+
const answerUser = (bestComment.user as Record<string, unknown>) || {};
69+
description += '<hr><h4>💡 最佳回答</h4>';
70+
description += `<p><strong>${answerUser.userName || '匿名'}</strong>:</p>`;
71+
description += `<p>${(bestComment.plainTextDescription as string) || ''}</p>`;
72+
}
73+
74+
return {
75+
title,
76+
link: `https://www.codefather.cn/qa/${item.id}`,
77+
description,
78+
pubDate: parseDate(item.createTime as number),
79+
author: user.userName as string,
80+
category: tags,
81+
upvotes: item.thumbNum as number,
82+
comments: item.commentNum as number,
83+
};
84+
});
85+
86+
return {
87+
title: `编程导航 - ${sortConfig.name}问答`,
88+
link: 'https://www.codefather.cn/qa',
89+
description: `编程导航社区${sortConfig.name}问答`,
90+
item: items,
91+
};
92+
}

0 commit comments

Comments
 (0)