diff --git a/lib/routes/buaa/lib/space/newbook.ts b/lib/routes/buaa/lib/space/newbook.ts index 3ef8cde73..810b8ef3c 100644 --- a/lib/routes/buaa/lib/space/newbook.ts +++ b/lib/routes/buaa/lib/space/newbook.ts @@ -5,7 +5,7 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import cache from '@/utils/cache'; import { art } from '@/utils/render'; -import path from 'path'; +import path from 'node:path'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); diff --git a/lib/routes/daily/discussed.ts b/lib/routes/daily/discussed.ts index 54a595727..23f80e4b5 100644 --- a/lib/routes/daily/discussed.ts +++ b/lib/routes/daily/discussed.ts @@ -1,9 +1,5 @@ import { Route } from '@/types'; -import { getData, getList, getRedirectedLink } from './utils.js'; - -const variables = { - first: 15, -}; +import { baseUrl, getData, getList } from './utils.js'; const query = ` query MostDiscussedFeed( @@ -19,6 +15,7 @@ const query = ` edges { node { ...FeedPost + contentHtml } } } @@ -33,6 +30,7 @@ const query = ` image readTime permalink + commentsPermalink summary createdAt numUpvotes @@ -52,37 +50,40 @@ const query = ` bio } `; -const graphqlQuery = { - query, - variables, -}; export const route: Route = { path: '/discussed', example: '/daily/discussed', radar: [ { - source: ['daily.dev/popular'], + source: ['app.daily.dev/discussed'], }, ], name: 'Most Discussed', maintainers: ['Rjnishant530'], handler, - url: 'daily.dev/popular', + url: 'app.daily.dev/discussed', }; -async function handler() { - const baseUrl = 'https://app.daily.dev/discussed'; - const data = await getData(graphqlQuery); - const list = getList(data); - const items = await getRedirectedLink(list); +async function handler(ctx) { + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; + const link = `${baseUrl}/discussed`; + + const data = await getData({ + query, + variables: { + first: limit, + }, + }); + const items = getList(data); + return { - title: 'Most Discussed', - link: baseUrl, + title: 'Real-time discussions in the developer community | daily.dev', + link, item: items, - description: 'Most Discussed Posts on Daily.dev', - logo: 'https://app.daily.dev/favicon-32x32.png', - icon: 'https://app.daily.dev/favicon-32x32.png', + description: 'Stay on top of real-time developer discussions on daily.dev. Join conversations happening now and engage with the most active community members.', + logo: `${baseUrl}/favicon-32x32.png`, + icon: `${baseUrl}/favicon-32x32.png`, language: 'en-us', }; } diff --git a/lib/routes/daily/index.ts b/lib/routes/daily/index.ts index 031f47c0a..40f0fdfbe 100644 --- a/lib/routes/daily/index.ts +++ b/lib/routes/daily/index.ts @@ -1,5 +1,5 @@ import { Route } from '@/types'; -import { getData, getList, getRedirectedLink } from './utils.js'; +import { baseUrl, getData, getList } from './utils.js'; const variables = { version: 11, @@ -28,6 +28,7 @@ const query = ` edges { node { ...FeedPost + contentHtml } } } @@ -42,6 +43,7 @@ const query = ` image readTime permalink + commentsPermalink summary createdAt numUpvotes @@ -72,27 +74,28 @@ export const route: Route = { example: '/daily', radar: [ { - source: ['daily.dev/popular'], + source: ['app.daily.dev/popular'], }, ], name: 'Popular', maintainers: ['Rjnishant530'], handler, - url: 'daily.dev/popular', + url: 'app.daily.dev/popular', }; async function handler() { - const baseUrl = 'https://app.daily.dev/popular'; + const link = `${baseUrl}/popular`; + const data = await getData(graphqlQuery); - const list = getList(data); - const items = await getRedirectedLink(list); + const items = getList(data); + return { - title: 'Popular', - link: baseUrl, + title: 'Popular posts on daily.dev', + link, item: items, - description: 'Popular Posts on Daily.dev', - logo: 'https://app.daily.dev/favicon-32x32.png', - icon: 'https://app.daily.dev/favicon-32x32.png', + description: 'daily.dev is the easiest way to stay updated on the latest programming news. Get the best content from the top tech publications on any topic you want.', + logo: `${baseUrl}/favicon-32x32.png`, + icon: `${baseUrl}/favicon-32x32.png`, language: 'en-us', }; } diff --git a/lib/routes/daily/namespace.ts b/lib/routes/daily/namespace.ts index f40a96813..1b57d03d5 100644 --- a/lib/routes/daily/namespace.ts +++ b/lib/routes/daily/namespace.ts @@ -2,7 +2,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'Daily.dev', - url: 'daily.dev', + url: 'app.daily.dev', categories: ['social-media'], lang: 'en', }; diff --git a/lib/routes/daily/source.ts b/lib/routes/daily/source.ts new file mode 100644 index 000000000..4594daa6d --- /dev/null +++ b/lib/routes/daily/source.ts @@ -0,0 +1,186 @@ +import { Route } from '@/types'; +import { baseUrl, getBuildId, getData, getList } from './utils'; +import ofetch from '@/utils/ofetch'; +import cache from '@/utils/cache'; +import { config } from '@/config'; + +interface Source { + id: string; + name: string; + handle: string; + image: string; + permalink: string; + description: string; + type: string; +} + +const sourceFeedQuery = ` +query SourceFeed($source: ID!, $loggedIn: Boolean! = false, $first: Int, $after: String, $ranking: Ranking, $supportedTypes: [String!]) { + page: sourceFeed( + source: $source + first: $first + after: $after + ranking: $ranking + supportedTypes: $supportedTypes + ) { + ...FeedPostConnection + } +} + +fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...FeedPost + pinnedAt + contentHtml + ...UserPost @include(if: $loggedIn) + } + } +} + +fragment FeedPost on Post { + ...FeedPostInfo + sharedPost { + id + title + image + readTime + permalink + commentsPermalink + createdAt + type + tags + source { + id + handle + permalink + image + } + slug + } + trending + feedMeta + collectionSources { + handle + image + } + numCollectionSources + updatedAt + slug +} + +fragment FeedPostInfo on Post { + id + title + image + readTime + permalink + commentsPermalink + createdAt + commented + bookmarked + views + numUpvotes + numComments + summary + bookmark { + remindAt + } + author { + id + name + image + username + permalink + } + type + tags + source { + id + handle + name + permalink + image + type + } + userState { + vote + flags { + feedbackDismiss + } + } + slug +} + +fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted +}`; + +export const route: Route = { + path: '/source/:sourceId', + example: '/daily/source/hn', + parameters: { + sourceId: 'The source id', + }, + radar: [ + { + source: ['app.daily.dev/sources/:sourceId'], + }, + ], + name: 'Source Posts', + maintainers: ['TonyRL'], + handler, + url: 'app.daily.dev', +}; + +async function handler(ctx) { + const sourceId = ctx.req.param('sourceId'); + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; + const link = `${baseUrl}/sources/${sourceId}`; + + const buildId = await getBuildId(); + + const userData = (await cache.tryGet(`daily:source:${sourceId}`, async () => { + const response = await ofetch(`${baseUrl}/_next/data/${buildId}/en/sources/${sourceId}.json`); + return response.pageProps.source; + })) as Source; + + const items = await cache.tryGet( + `daily:source:${sourceId}:posts`, + async () => { + const edges = await getData({ + query: sourceFeedQuery, + variables: { + source: sourceId, + supportedTypes: ['article', 'video:youtube', 'collection'], + period: 30, + first: limit, + after: '', + loggedIn: false, + }, + }); + return getList(edges); + }, + config.cache.routeExpire, + false + ); + + return { + title: `${userData.name} posts on daily.dev`, + description: userData.description, + link, + item: items, + image: userData.image, + logo: userData.image, + icon: userData.image, + language: 'en-us', + }; +} diff --git a/lib/routes/daily/upvoted.ts b/lib/routes/daily/upvoted.ts index 68a2a19d6..8326d0618 100644 --- a/lib/routes/daily/upvoted.ts +++ b/lib/routes/daily/upvoted.ts @@ -1,5 +1,5 @@ import { Route } from '@/types'; -import { getData, getList, getRedirectedLink } from './utils.js'; +import { baseUrl, getData, getList } from './utils.js'; const variables = { period: 7, @@ -21,6 +21,7 @@ const query = ` edges { node { ...FeedPost + contentHtml } } } @@ -35,6 +36,7 @@ const query = ` image readTime permalink + commentsPermalink summary createdAt numUpvotes @@ -56,37 +58,35 @@ const query = ` `; -const graphqlQuery = { - query, - variables, -}; - export const route: Route = { path: '/upvoted', example: '/daily/upvoted', radar: [ { - source: ['daily.dev/popular'], + source: ['app.daily.dev/upvoted'], }, ], name: 'Most upvoted', maintainers: ['Rjnishant530'], handler, - url: 'daily.dev/popular', + url: 'app.daily.dev/upvoted', }; async function handler() { - const baseUrl = 'https://app.daily.dev/upvoted'; - const data = await getData(graphqlQuery); - const list = getList(data); - const items = await getRedirectedLink(list); + const link = `${baseUrl}/upvoted`; + const data = await getData({ + query, + variables, + }); + const items = getList(data); + return { - title: 'Most Upvoted', - link: baseUrl, + title: 'Most upvoted posts for developers | daily.dev', + link, item: items, - description: 'Most Upvoted Posts on Daily.dev', - logo: 'https://app.daily.dev/favicon-32x32.png', - icon: 'https://app.daily.dev/favicon-32x32.png', + description: 'Find the most upvoted developer posts on daily.dev. Explore top-rated content in coding, tutorials, and tech news from the largest developer network in the world.', + logo: `${baseUrl}/favicon-32x32.png`, + icon: `${baseUrl}/favicon-32x32.png`, language: 'en-us', }; } diff --git a/lib/routes/daily/user.ts b/lib/routes/daily/user.ts index 7abaf15a0..cdf738368 100644 --- a/lib/routes/daily/user.ts +++ b/lib/routes/daily/user.ts @@ -1,13 +1,8 @@ import { Route } from '@/types'; -import { baseUrl, getBuildId, getData } from './utils'; +import { baseUrl, getBuildId, getData, getList } from './utils'; import ofetch from '@/utils/ofetch'; import cache from '@/utils/cache'; import { config } from '@/config'; -import { parseDate } from '@/utils/parse-date'; -import { art } from '@/utils/render'; -import path from 'path'; -import { getCurrentPath } from '@/utils/helpers'; -const __dirname = getCurrentPath(import.meta.url); const userPostQuery = ` query AuthorFeed( @@ -155,20 +150,18 @@ const userPostQuery = ` downvoted }`; -const render = (data) => art(path.join(__dirname, 'templates/posts.art'), data); - export const route: Route = { path: '/user/:userId', example: '/daily/user/kramer', radar: [ { - source: ['daily.dev/:userId/posts', 'daily.dev/:userId'], + source: ['app.daily.dev/:userId/posts', 'app.daily.dev/:userId'], }, ], name: 'User Posts', maintainers: ['TonyRL'], handler, - url: 'daily.dev', + url: 'app.daily.dev', }; async function handler(ctx) { @@ -178,12 +171,12 @@ async function handler(ctx) { const buildId = await getBuildId(); const userData = await cache.tryGet(`daily:user:${userId}`, async () => { - const resposne = await ofetch(`${baseUrl}/_next/data/${buildId}/en/${userId}.json`, { + const response = await ofetch(`${baseUrl}/_next/data/${buildId}/en/${userId}.json`, { query: { userId, }, }); - return resposne.pageProps; + return response.pageProps; }); const user = (userData as any).user; @@ -198,17 +191,7 @@ async function handler(ctx) { loggedIn: false, }, }); - return edges.map(({ node }) => ({ - title: node.title, - description: render({ - image: node.image, - content: node.contentHtml?.replaceAll('\n', '
') ?? node.summary, - }), - link: node.permalink, - author: node.author?.name, - category: node.tags, - pubDate: parseDate(node.createdAt), - })); + return getList(edges); }, config.cache.routeExpire, false diff --git a/lib/routes/daily/utils.ts b/lib/routes/daily/utils.ts index 8f203c744..4576a8695 100644 --- a/lib/routes/daily/utils.ts +++ b/lib/routes/daily/utils.ts @@ -2,10 +2,15 @@ import { parseDate } from '@/utils/parse-date'; import ofetch from '@/utils/ofetch'; import cache from '@/utils/cache'; import { config } from '@/config'; +import { art } from '@/utils/render'; +import path from 'node:path'; +import { getCurrentPath } from '@/utils/helpers'; +const __dirname = getCurrentPath(import.meta.url); -const baseUrl = 'https://app.daily.dev'; +export const baseUrl = 'https://app.daily.dev'; +const gqlUrl = `https://api.daily.dev/graphql`; -const getBuildId = () => +export const getBuildId = () => cache.tryGet( 'daily:buildId', async () => { @@ -17,40 +22,30 @@ const getBuildId = () => false ); -const getData = async (graphqlQuery) => { - const response = await ofetch(`${baseUrl}/api/graphql`, { +export const getData = async (graphqlQuery) => { + const response = await ofetch(gqlUrl, { method: 'POST', body: graphqlQuery, }); return response.data.page.edges; }; -const getList = (data) => - data.map((value) => { - const { id, title, image, permalink, summary, createdAt, numUpvotes, author, tags, numComments } = value.node; - const pubDate = parseDate(createdAt); - return { - id, - title, - link: permalink, - description: summary, - author: author?.name, - itunes_item_image: image, - pubDate, - upvotes: numUpvotes, - comments: numComments, - category: tags, - }; - }); +const render = (data) => art(path.join(__dirname, 'templates/posts.art'), data); -const getRedirectedLink = (data) => - Promise.all( - data.map((v) => - cache.tryGet(v.link, async () => { - const resp = await ofetch.raw(v.link); - return { ...v, link: resp.headers.get('location') }; - }) - ) - ); - -export { baseUrl, getBuildId, getData, getList, getRedirectedLink }; +export const getList = (edges) => + edges.map(({ node }) => ({ + id: node.id, + title: node.title, + link: node.commentsPermalink ?? node.permalink, + guid: node.permalink, + description: render({ + image: node.image, + content: node.contentHtml?.replaceAll('\n', '
') ?? node.summary, + }), + author: node.author?.name, + itunes_item_image: node.image, + pubDate: parseDate(node.createdAt), + upvotes: node.numUpvotes, + comments: node.numComments, + category: node.tags, + })); diff --git a/lib/routes/espn/news.ts b/lib/routes/espn/news.ts index 154cb72cd..0187cd06c 100644 --- a/lib/routes/espn/news.ts +++ b/lib/routes/espn/news.ts @@ -2,7 +2,7 @@ import { Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import * as cheerio from 'cheerio'; -import path from 'path'; +import path from 'node:path'; import { getCurrentPath } from '@/utils/helpers'; import { art } from '@/utils/render'; diff --git a/lib/routes/imdb/chart.ts b/lib/routes/imdb/chart.ts index 4d1611280..117454065 100644 --- a/lib/routes/imdb/chart.ts +++ b/lib/routes/imdb/chart.ts @@ -3,7 +3,7 @@ import ofetch from '@/utils/ofetch'; import * as cheerio from 'cheerio'; import type { Context } from 'hono'; import { ChartTitleSearchConnection } from './types'; -import path from 'path'; +import path from 'node:path'; import { getCurrentPath } from '@/utils/helpers'; import { art } from '@/utils/render'; diff --git a/lib/routes/jiuyangongshe/community.ts b/lib/routes/jiuyangongshe/community.ts index 05138854f..d739cd583 100644 --- a/lib/routes/jiuyangongshe/community.ts +++ b/lib/routes/jiuyangongshe/community.ts @@ -4,7 +4,7 @@ import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import md5 from '@/utils/md5'; -import path from 'path'; +import path from 'node:path'; import { getCurrentPath } from '@/utils/helpers'; import { art } from '@/utils/render'; diff --git a/lib/routes/mi/utils.ts b/lib/routes/mi/utils.ts index a0839f489..2fc2c90d2 100644 --- a/lib/routes/mi/utils.ts +++ b/lib/routes/mi/utils.ts @@ -9,7 +9,7 @@ import 'dayjs/locale/zh-cn'; import localizedFormat from 'dayjs/plugin/localizedFormat'; import timezone from 'dayjs/plugin/timezone'; import utc from 'dayjs/plugin/utc'; -import path from 'path'; +import path from 'node:path'; import { CrowdfundingData, CrowdfundingDetailData, CrowdfundingDetailInfo, CrowdfundingItem, CrowdfundingList, DataResponse } from './types'; dayjs.extend(localizedFormat); diff --git a/lib/routes/theblockbeats/index.ts b/lib/routes/theblockbeats/index.ts index 4fc1112c7..a61a6fc9f 100644 --- a/lib/routes/theblockbeats/index.ts +++ b/lib/routes/theblockbeats/index.ts @@ -3,7 +3,7 @@ import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -import path from 'path'; +import path from 'node:path'; import { art } from '@/utils/render'; import { getCurrentPath } from '@/utils/helpers'; diff --git a/lib/routes/vice/topic.ts b/lib/routes/vice/topic.ts index 339655ad8..51b0f977f 100644 --- a/lib/routes/vice/topic.ts +++ b/lib/routes/vice/topic.ts @@ -3,7 +3,7 @@ import ofetch from '@/utils/ofetch'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import cache from '@/utils/cache'; -import path from 'path'; +import path from 'node:path'; import { getCurrentPath } from '@/utils/helpers'; import { art } from '@/utils/render'; diff --git a/lib/routes/wellcee/rent.ts b/lib/routes/wellcee/rent.ts index dc67f30c9..9c092b40d 100644 --- a/lib/routes/wellcee/rent.ts +++ b/lib/routes/wellcee/rent.ts @@ -7,7 +7,7 @@ import { parseDate } from '@/utils/parse-date'; import { baseUrl, getCitys, getDistricts } from './utils'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import { art } from '@/utils/render'; -import path from 'path'; +import path from 'node:path'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url);