diff --git a/lib/routes/aeon/category.ts b/lib/routes/aeon/category.ts
index bc87a7e54..714bd4ec5 100644
--- a/lib/routes/aeon/category.ts
+++ b/lib/routes/aeon/category.ts
@@ -1,8 +1,7 @@
import type { Route } from '@/types';
import ofetch from '@/utils/ofetch';
-import { parseDate } from '@/utils/parse-date';
-import { getBuildId, getData } from './utils';
+import { getData } from './utils';
export const route: Route = {
path: '/category/:category',
@@ -38,32 +37,79 @@ export const route: Route = {
handler,
};
+const ENDPOINT = 'https://api.aeonmedia.co/graphql';
+const LIST_BY_SECTION = /* GraphQL */ `
+query getAeonArticlesBySection($section: String!, $sortField: ArticleSortEnum = published_at, $afterCursor: String, $tag: String) {
+ section(site: aeon, slug: $section) {
+ slug
+ title
+ metaDescription
+ }
+ articles(
+ site: aeon
+ section: $section
+ status: [published]
+ tag: $tag
+ sort: {field: $sortField, order: desc}
+ after: $afterCursor
+ first: 24
+ ) {
+ nodes {
+ slug
+ ...aeonArticleCardFragment
+ }
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ }
+}
+
+fragment aeonArticleCardFragment on Article {
+ id
+ title
+ slug
+ type
+ standfirstLong
+ authors { name }
+ image { url }
+ primaryTopic { title }
+ section { slug }
+}
+`;
+
async function handler(ctx) {
const category = ctx.req.param('category').toLowerCase();
const url = `https://aeon.co/category/${category}`;
- const buildId = await getBuildId();
- const response = await ofetch(`https://aeon.co/_next/data/${buildId}/${category}.json`);
+ const response = await ofetch(ENDPOINT, {
+ method: 'POST',
+ body: {
+ operationName: 'getAeonArticlesBySection',
+ query: LIST_BY_SECTION,
+ variables: {
+ section: category,
+ },
+ },
+ });
- const section = response.pageProps.section;
-
- const list = section.articles.edges.map(({ node }) => ({
+ const list = response.data.articles.nodes.map((node) => ({
title: node.title,
description: node.standfirstLong,
- author: node.authors.map((author) => author.displayName).join(', '),
+ author: node.authors.map((author) => author.name).join(', '),
link: `https://aeon.co/${node.type}s/${node.slug}`,
- pubDate: parseDate(node.createdAt),
- category: [node.section.title, ...node.topics.map((topic) => topic.title)],
+ category: node.primaryTopic.title,
image: node.image.url,
type: node.type,
+ section: node.section.slug,
slug: node.slug,
}));
const items = await getData(list);
return {
- title: `AEON | ${section.title}`,
+ title: `AEON | ${response.data.section.title}`,
link: url,
- description: section.metaDescription,
+ description: response.data.section.metaDescription,
item: items,
};
}
diff --git a/lib/routes/aeon/type.ts b/lib/routes/aeon/type.ts
index 581c25fa8..40fbb2d61 100644
--- a/lib/routes/aeon/type.ts
+++ b/lib/routes/aeon/type.ts
@@ -1,8 +1,39 @@
import type { Route } from '@/types';
import ofetch from '@/utils/ofetch';
-import { parseDate } from '@/utils/parse-date';
-import { getBuildId, getData } from './utils';
+import { getData } from './utils';
+
+const ENDPOINT = 'https://api.aeonmedia.co/graphql';
+const LIST_BY_TYPE = /* GraphQL */ `
+query getAeonArticlesByType($type: [ArticleTypeEnum!], $sortField: ArticleSortEnum = published_at, $afterCursor: String, $tag: String) {
+ articles(
+ site: aeon
+ type: $type
+ status: [published]
+ tag: $tag
+ sort: {field: $sortField, order: desc}
+ after: $afterCursor
+ first: 12
+ ) {
+ nodes {
+ slug
+ ...aeonArticleCardFragment
+ }
+ }
+}
+
+fragment aeonArticleCardFragment on Article {
+ id
+ title
+ slug
+ type
+ standfirstLong
+ authors { name }
+ image { url }
+ primaryTopic { title }
+ section { slug }
+}
+`;
export const route: Route = {
path: '/:type',
@@ -43,19 +74,25 @@ async function handler(ctx) {
const type = ctx.req.param('type');
const capitalizedType = type.charAt(0).toUpperCase() + type.slice(1);
- const buildId = await getBuildId();
const url = `https://aeon.co/${type}`;
- const response = await ofetch(`https://aeon.co/_next/data/${buildId}/${type}.json`);
+ const response = await ofetch(ENDPOINT, {
+ method: 'POST',
+ body: {
+ query: LIST_BY_TYPE,
+ variables: { type: [type.slice(0, -1)], sortField: 'published_at' },
+ operationName: 'getAeonArticlesByType',
+ },
+ });
- const list = response.pageProps.articles.map((node) => ({
+ const list = response.data.articles.nodes.map((node) => ({
title: node.title,
description: node.standfirstLong,
- author: node.authors.map((author) => author.displayName).join(', '),
- link: `https://aeon.co/${node.type}s/${node.slug}`,
- pubDate: parseDate(node.createdAt),
- category: [node.section.title, ...node.topics.map((topic) => topic.title)],
+ author: node.authors.map((author) => author.name).join(', '),
+ link: `https://aeon.co/${type}/${node.slug}`,
+ category: node.primaryTopic.title,
image: node.image.url,
type: node.type,
+ section: node.section.slug,
slug: node.slug,
}));
diff --git a/lib/routes/aeon/utils.tsx b/lib/routes/aeon/utils.tsx
index 7cb63fba2..7500e7eb9 100644
--- a/lib/routes/aeon/utils.tsx
+++ b/lib/routes/aeon/utils.tsx
@@ -2,23 +2,36 @@ import { load } from 'cheerio';
import { raw } from 'hono/html';
import { renderToString } from 'hono/jsx/dom/server';
-import { config } from '@/config';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
-export const getBuildId = () =>
- cache.tryGet(
- 'aeon:buildId',
- async () => {
- const response = await ofetch('https://aeon.co');
- const $ = load(response);
- const nextData = JSON.parse($('script#__NEXT_DATA__').text());
- return nextData.buildId;
- },
- config.cache.routeExpire,
- false
- );
+const ENDPOINT = 'https://api.aeonmedia.co/graphql';
+
+const ESSAY = /* GraphQL */ `
+query getAeonEssay($slug: String!) {
+ essay(slug: $slug) {
+ publishedAt
+ updatedAt
+ authors { name authorBio }
+ audioUrl
+ image { url alt caption }
+ body
+ }
+}`;
+
+const VIDEO = /* GraphQL */ `
+query getAeonVideo($slug: String!, $site: SiteEnum!) {
+ video(slug: $slug, site: $site) {
+ publishedAt
+ updatedAt
+ authors { name authorBio }
+ hoster
+ hosterId
+ credits
+ description
+ }
+}`;
const renderVideoDescription = (article) => {
let video = article.hosterId;
@@ -44,7 +57,7 @@ const renderEssayDescription = ({ banner, authorsBio, content }) =>
{banner?.url ? (
- {banner.caption ?
' + author.name + author.authorBio.replaceAll(/^
/g, ' ')).join(''); + const authorsBio = renderToString( + <> +
+ {author.name} + {raw(author.authorBio.replaceAll(/^
/g, ' '))} +
+ ))} +