diff --git a/lib/routes/cockroachlabs/blog.ts b/lib/routes/cockroachlabs/blog.ts new file mode 100644 index 000000000..cc28c09b4 --- /dev/null +++ b/lib/routes/cockroachlabs/blog.ts @@ -0,0 +1,113 @@ +import { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { load } from 'cheerio'; +import cache from '@/utils/cache'; +import { parseDate } from '@/utils/parse-date'; + +export const route: Route = { + name: 'Blogs', + maintainers: ['CookiePieWw'], + categories: ['programming'], + path: '/blog/:category?', + example: '/cockroachlabs/blog/engineering', + parameters: { category: 'Blog category, e.g., engineering. Subscribe all recent articles if empty.' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['cockroachlabs.com/blog/:category', 'cockroachlabs.com/blog'], + target: '/blog', + }, + ], + handler, +}; + +async function handler(ctx) { + const category = ctx.req.param('category'); + const rootUrl = 'https://www.cockroachlabs.com'; + const currentUrl = `${rootUrl}/blog${category ? `/${category}/` : '/'}`; + + const webpage = await ofetch(currentUrl); + const html = load(webpage); + + // Title article: + // + //

+ // Title... + //

+ //
+ const titleH2Element = html('[class="mb-3 truncate text-display-md font-semibold tracking-tight md:max-w-full md:text-white"]'); + + // Left articles: + // + //

+ // Title.. + //

+ //
+ const leftArticles = html('a > p[class="mb-2 line-clamp-2 text-lg font-semibold leading-5"]'); + const articleList = titleH2Element.add(leftArticles).map((_, element) => { + const title = html(element).text(); + const link = `${rootUrl}${html(element).parent('a').attr('href')}`; + return { title, link }; + }); + + const items = await Promise.all( + articleList.toArray().map((article) => + cache.tryGet(article.link, async () => { + const response = await ofetch(article.link); + const $ = load(response); + + //
+ // ..multiple
///

.. + //

+ const content = $('article.blog-content').html() || ''; + + //
+ //
+ //

+ // Last edited on June 13, 2025 + //

+ //
+ // ... + //
+ const dateText = $(String.raw`div.mt-4.flex.flex-col.items-center.justify-center.gap-1.sm\:flex-row.sm\:gap-4`) + .find('p') + .first() + .text() + .match(/Last edited on (.+)/)?.[1]; + + let pubDate: Date | undefined; + if (dateText) { + try { + const date = new Date(dateText); + if (!Number.isNaN(date.getTime())) { + pubDate = parseDate(date.toISOString().split('T')[0]); + } + } catch { + // Ignore parsing errors + } + } + + return { + title: article.title, + link: article.link, + description: content, + pubDate, + }; + }) + ) + ); + + return { + title: `Cockroach Labs Blog${category ? ` - ${category}` : ''}`, + link: currentUrl, + item: items, + description: 'Cockroach Labs Blog', + }; +} diff --git a/lib/routes/cockroachlabs/namespace.ts b/lib/routes/cockroachlabs/namespace.ts new file mode 100644 index 000000000..bfa3bed46 --- /dev/null +++ b/lib/routes/cockroachlabs/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Cockroach Labs', + url: 'cockroachlabs.com', + lang: 'en', +};