rsshub
Version:
Make RSS Great Again!
240 lines (238 loc) • 9.44 kB
JavaScript
import { t as rofetch } from "./ofetch-C3ts-Hud.mjs";
import "./types-DNj6Etbg.mjs";
import { t as parseDate } from "./parse-date-Cr0wQjV_.mjs";
import { t as cache_default } from "./cache-BkqOokyU.mjs";
import { load } from "cheerio";
//#region lib/routes/tidb/blog.ts
const escapeHtml = (text) => text?.replaceAll("&", "&")?.replaceAll("<", "<")?.replaceAll(">", ">")?.replaceAll("\"", """)?.replaceAll("'", "'") ?? text;
const parseTextChildren = (children) => children.map((child) => escapeHtml(child.text)).join("");
const parseImageNode = (node) => {
const titleAttr = node.title ? ` title="${escapeHtml(node.title)}"` : "";
const altAttr = node.alt ? ` alt="${escapeHtml(node.alt)}"` : "";
const styleAttr = node.size ? ` style="width:${node.size.width}px;height:${node.size.height}px;"` : "";
return `<img src="${escapeHtml(node.url)}"${titleAttr}${altAttr}${styleAttr}>`;
};
const parseListItemNode = (listItem) => `<li>${parseContentToHtml(listItem.children)}</li>`;
const parseListNode = (node) => {
const tag = node.ordered ? "ol" : "ul";
return `<${tag}${node.ordered && node.start !== 1 ? ` start="${node.start}"` : ""}>${node.children.map((item) => parseListItemNode(item)).join("")}</${tag}>`;
};
const parseParagraphChildren = (children) => children.map((child) => {
if (child.text !== void 0) return escapeHtml(child.text);
if (child.type === "image") return parseImageNode(child);
return "";
}).join("");
const parseContentToHtml = (content) => content?.map((node) => {
switch (node.type) {
case "paragraph": return `<p>${parseParagraphChildren(node.children)}</p>`;
case "image": return parseImageNode(node);
case "heading": return `<h${node.depth}>${parseTextChildren(node.children)}</h${node.depth ?? ""}>`;
case "code": return `<pre><code${node.lang ? ` class="language-${node.lang}"` : ""}>${parseTextChildren(node.children)}</code></pre>`;
case "list": return parseListNode(node);
case "blockquote": return `<blockquote>${parseContentToHtml(node.children)}</blockquote>`;
default: return "";
}
}).join("") ?? "";
const handler = async (ctx) => {
const { category = "latest" } = ctx.req.param();
const limit = Number(ctx.req.query("limit") ?? "20");
const baseUrl = "https://tidb.net";
const targetUrl = new URL(`blog${category === "latest" ? "" : `/c/${category}`}`, baseUrl).href;
const targetResponse = await rofetch(targetUrl);
const buildId = targetResponse.match(/"buildId":"(.*?)"/)?.[1];
if (!buildId) throw new Error("Build ID not found.");
const $ = load(targetResponse);
const language = $("html").attr("lang") ?? "zh";
const apiUrl = new URL(`_next/data/${buildId}/${language}/blog${category === "latest" ? "" : `/c/${category}`}.json`, baseUrl).href;
let items = (await rofetch(apiUrl, { query: { latest: true } })).pageProps.blogs.content.slice(0, limit).map((item) => {
const title = item.title;
const description = item.summary;
const pubDate = item.publishedAt;
const linkUrl = item.slug ? `blog/${item.slug}` : void 0;
const categories = [...new Set([item.category?.name, ...(item.tags ?? []).map((c) => c.name)].filter(Boolean))];
const authors = item.author?.username ? [{
name: item.author.username,
url: new URL(`u/${item.author.username}`, baseUrl).href,
avatar: item.author.avatarURL
}] : void 0;
const guid = item.slug ?? "";
const updated = item.lastModifiedAt ?? pubDate;
return {
title,
description,
pubDate: pubDate ? parseDate(pubDate) : void 0,
link: linkUrl ? new URL(linkUrl, baseUrl).href : void 0,
category: categories,
author: authors,
guid,
id: guid,
content: {
html: description,
text: description
},
updated: updated ? parseDate(updated) : void 0,
language
};
});
items = await Promise.all(items.map((item) => {
if (!item.link) return item;
return cache_default.tryGet(item.link, async () => {
const detailUrl = new URL(`blog/api/posts/${item.guid}/detail`, baseUrl).href;
const detailResponse = await rofetch(detailUrl, { query: { visit: true } });
const title = detailResponse.title;
const description = detailResponse.content ? parseContentToHtml(JSON.parse(detailResponse.content)) : item.description;
const pubDate = detailResponse.publishedAt;
const linkUrl = `blog/${detailResponse.slug}`;
const categories = [...new Set([detailResponse.category?.name, ...(detailResponse.tags ?? []).map((c) => c.name)].filter(Boolean))];
const authors = detailResponse.author?.username ? [{
name: detailResponse.author.username,
url: new URL(`u/${detailResponse.author.username}`, baseUrl).href,
avatar: detailResponse.author.avatarURL
}] : void 0;
const guid = `tidb-blog-${detailResponse.slug}`;
const updated = detailResponse.lastModifiedAt ?? pubDate;
const processedItem = {
title,
description,
pubDate: pubDate ? parseDate(pubDate) : void 0,
link: new URL(linkUrl, baseUrl).href,
category: categories,
author: authors,
guid,
id: guid,
content: {
html: description,
text: description
},
updated: updated ? parseDate(updated) : void 0,
language
};
return {
...item,
...processedItem
};
});
}));
const title = $("title").text();
return {
title,
description: $("meta[property=\"og:description\"]").attr("content"),
link: targetUrl,
item: items,
allowEmpty: true,
image: $("meta[property=\"og:image\"]").attr("content"),
author: title.split(/\|/).pop()?.trim(),
language,
id: targetUrl
};
};
const route = {
path: "/blog/c/:category?",
name: "专栏分类",
url: "tidb.net",
maintainers: ["nczitzk"],
handler,
example: "/tidb/blog/c/latest",
parameters: { category: {
description: "分类,默认为 `latest`,即全部文章,可在对应分类页 URL 中找到",
options: [
{
label: "全部文章",
value: "latest"
},
{
label: "管理与运维",
value: "management-and-operation"
},
{
label: "实践案例",
value: "practical-case"
},
{
label: "架构选型",
value: "architecture-selection"
},
{
label: "原理解读",
value: "principle-interpretation"
},
{
label: "应用开发",
value: "application-development"
},
{
label: "社区动态",
value: "community-feeds"
}
]
} },
description: `::: tip
订阅 [管理与运维](https://tidb.net/blog/c/management-and-operation),其源网址为 \`https://tidb.net/blog/c/management-and-operation\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/tidb/blog/c/management-and-operation\`](https://rsshub.app/tidb/blog/c/management-and-operation)。
:::
| 分类 | ID |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [全部文章](https://tidb.net/blog) | [latest](https://rsshub.app/tidb/blog) |
| [管理与运维](https://tidb.net/blog/c/management-and-operation) | [management-and-operation](https://rsshub.app/tidb/blog/c/management-and-operation) |
| [实践案例](https://tidb.net/blog/c/practical-case) | [practical-case](https://rsshub.app/tidb/blog/c/practical-case) |
| [架构选型](https://tidb.net/blog/c/architecture-selection) | [architecture-selection](https://rsshub.app/tidb/blog/c/architecture-selection) |
| [原理解读](https://tidb.net/blog/c/principle-interpretation) | [principle-interpretation](https://rsshub.app/tidb/blog/c/principle-interpretation) |
| [应用开发](https://tidb.net/blog/c/application-development) | [application-development](https://rsshub.app/tidb/blog/c/application-development) |
| [社区动态](https://tidb.net/blog/c/community-feeds) | [community-feeds](https://rsshub.app/tidb/blog/c/community-feeds) |`,
categories: ["programming"],
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportRadar: true,
supportBT: false,
supportPodcast: false,
supportScihub: false
},
radar: [
{
source: ["tidb.net/blog", "tidb.net/blog/c/:category"],
target: (params) => {
const category = params.category;
return `/tidb/blog/c${category ? `/${category}` : ""}`;
}
},
{
title: "全部文章",
source: ["tidb.net/blog"],
target: "/blog/c/latest"
},
{
title: "管理与运维",
source: ["tidb.net/blog/c/management-and-operation"],
target: "/blog/c/management-and-operation"
},
{
title: "实践案例",
source: ["tidb.net/blog/c/practical-case"],
target: "/blog/c/practical-case"
},
{
title: "架构选型",
source: ["tidb.net/blog/c/architecture-selection"],
target: "/blog/c/architecture-selection"
},
{
title: "原理解读",
source: ["tidb.net/blog/c/principle-interpretation"],
target: "/blog/c/principle-interpretation"
},
{
title: "应用开发",
source: ["tidb.net/blog/c/application-development"],
target: "/blog/c/application-development"
},
{
title: "社区动态",
source: ["tidb.net/blog/c/community-feeds"],
target: "/blog/c/community-feeds"
}
],
view: 0
};
//#endregion
export { handler, route };