From 238955b5feb0f3f17be91dd240152c988b900dae Mon Sep 17 00:00:00 2001 From: Nishant Singh <57475999+Rjnishant530@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:12:10 +0530 Subject: [PATCH] fix(daily dev): fixed routes and added one new one (#18021) * add squads * style: auto format * null check * fix: add null checks for items * changes * trigger * use params * fix: add categories --------- Co-authored-by: rjnishant530 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/routes/alistapart/index.ts | 6 +- lib/routes/alistapart/topic.ts | 1 + lib/routes/daily/discussed.ts | 46 ++++- lib/routes/daily/index.ts | 101 ----------- lib/routes/daily/popular.ts | 182 ++++++++++++++++++++ lib/routes/daily/source.ts | 19 ++- lib/routes/daily/squads.ts | 266 +++++++++++++++++++++++++++++ lib/routes/daily/upvoted.ts | 157 +++++++++++++---- lib/routes/daily/user.ts | 20 ++- lib/routes/daily/utils.ts | 56 +++--- lib/routes/stockedge/daily-news.ts | 3 + lib/routes/stockedge/utils.ts | 15 +- 12 files changed, 698 insertions(+), 174 deletions(-) delete mode 100644 lib/routes/daily/index.ts create mode 100644 lib/routes/daily/popular.ts create mode 100644 lib/routes/daily/squads.ts diff --git a/lib/routes/alistapart/index.ts b/lib/routes/alistapart/index.ts index b91a9e48f6fa85..c603423017349f 100644 --- a/lib/routes/alistapart/index.ts +++ b/lib/routes/alistapart/index.ts @@ -3,16 +3,18 @@ import { getData, getList } from './utils'; export const route: Route = { path: '/', + categories: ['programming'], radar: [ { source: ['alistapart.com/articles/'], - target: '', + target: '/', }, ], - name: 'Unknown', + name: 'Home Feed', maintainers: ['Rjnishant530'], handler, url: 'alistapart.com/articles/', + example: '/alistapart', }; async function handler() { diff --git a/lib/routes/alistapart/topic.ts b/lib/routes/alistapart/topic.ts index c5c24d74ec41ed..13fad6b4c1bf1d 100644 --- a/lib/routes/alistapart/topic.ts +++ b/lib/routes/alistapart/topic.ts @@ -17,6 +17,7 @@ export const route: Route = { radar: [ { source: ['alistapart.com/blog/topic/:topic'], + target: '/:topic', }, ], name: 'Topics', diff --git a/lib/routes/daily/discussed.ts b/lib/routes/daily/discussed.ts index 23f80e4b57ba9a..cdc7027bf79bf6 100644 --- a/lib/routes/daily/discussed.ts +++ b/lib/routes/daily/discussed.ts @@ -1,5 +1,5 @@ -import { Route } from '@/types'; -import { baseUrl, getData, getList } from './utils.js'; +import { Route, ViewType } from '@/types'; +import { baseUrl, getData, getList, variables } from './utils.js'; const query = ` query MostDiscussedFeed( @@ -52,8 +52,9 @@ const query = ` `; export const route: Route = { - path: '/discussed', - example: '/daily/discussed', + path: '/discussed/:period?/:innerSharedContent?/:dateSort?', + example: '/daily/discussed/30', + view: ViewType.Articles, radar: [ { source: ['app.daily.dev/discussed'], @@ -63,19 +64,52 @@ export const route: Route = { maintainers: ['Rjnishant530'], handler, url: 'app.daily.dev/discussed', + parameters: { + innerSharedContent: { + description: 'Where to Fetch inner Shared Posts instead of original', + default: 'false', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + dateSort: { + description: 'Sort posts by publication date instead of popularity', + default: 'true', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + period: { + description: 'Period of Lookup', + default: '7', + options: [ + { value: '7', label: 'Last Week' }, + { value: '30', label: 'Last Month' }, + { value: '365', label: 'Last Year' }, + ], + }, + }, }; async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; - const link = `${baseUrl}/discussed`; + const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; + const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true; + const period = ctx.req.param('period') ? Number.parseInt(ctx.req.param('period'), 10) : 7; + + const link = `${baseUrl}/posts/discussed`; const data = await getData({ query, variables: { + ...variables, first: limit, + period, }, }); - const items = getList(data); + const items = getList(data, innerSharedContent, dateSort); return { title: 'Real-time discussions in the developer community | daily.dev', diff --git a/lib/routes/daily/index.ts b/lib/routes/daily/index.ts deleted file mode 100644 index 40f0fdfbeaef7d..00000000000000 --- a/lib/routes/daily/index.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Route } from '@/types'; -import { baseUrl, getData, getList } from './utils.js'; - -const variables = { - version: 11, - ranking: 'POPULARITY', - first: 15, -}; - -const query = ` - query AnonymousFeed( - $first: Int - $ranking: Ranking - $version: Int - $supportedTypes: [String!] = ["article","share","freeform"] - ) { - page: anonymousFeed( - first: $first - ranking: $ranking - version: $version - supportedTypes: $supportedTypes - ) { - ...FeedPostConnection - } - } - - fragment FeedPostConnection on PostConnection { - edges { - node { - ...FeedPost - contentHtml - } - } - } - - fragment FeedPost on Post { - ...SharedPostInfo - } - - fragment SharedPostInfo on Post { - id - title - image - readTime - permalink - commentsPermalink - summary - createdAt - numUpvotes - numComments - author { - ...UserShortInfo - } - tags - } - - fragment UserShortInfo on User { - id - name - image - permalink - username - bio - } -`; - -const graphqlQuery = { - query, - variables, -}; - -export const route: Route = { - path: '/', - example: '/daily', - radar: [ - { - source: ['app.daily.dev/popular'], - }, - ], - name: 'Popular', - maintainers: ['Rjnishant530'], - handler, - url: 'app.daily.dev/popular', -}; - -async function handler() { - const link = `${baseUrl}/popular`; - - const data = await getData(graphqlQuery); - const items = getList(data); - - return { - title: 'Popular posts on daily.dev', - link, - item: items, - 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/popular.ts b/lib/routes/daily/popular.ts new file mode 100644 index 00000000000000..32e654ec7f282c --- /dev/null +++ b/lib/routes/daily/popular.ts @@ -0,0 +1,182 @@ +import { Route, ViewType } from '@/types'; +import { baseUrl, getData, getList, variables } from './utils.js'; + +const query = ` + query AnonymousFeed( + $loggedIn: Boolean! = false + $first: Int + $after: String + $ranking: Ranking + $version: Int + $supportedTypes: [String!] = ["article","share","freeform","video:youtube","collection"] + ) { + page: anonymousFeed( + first: $first + after: $after + ranking: $ranking + version: $version + supportedTypes: $supportedTypes + ) { + ...FeedPostConnection + } + } + + fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...FeedPost + 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 + clickbaitTitleDetected + } + 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 + clickbaitTitleDetected + } + + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted + } +`; + +export const route: Route = { + path: '/popular/:innerSharedContent?/:dateSort?', + example: '/daily/popular', + view: ViewType.Articles, + radar: [ + { + source: ['app.daily.dev/popular'], + }, + ], + parameters: { + innerSharedContent: { + description: 'Where to Fetch inner Shared Posts instead of original', + default: 'false', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + dateSort: { + description: 'Sort posts by publication date instead of popularity', + default: 'true', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + }, + name: 'Popular', + maintainers: ['Rjnishant530'], + handler, + url: 'app.daily.dev/popular', +}; + +async function handler(ctx) { + const link = `${baseUrl}/posts`; + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; + const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; + const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true; + + const data = await getData({ + query, + variables: { + ...variables, + ranking: 'POPULARITY', + first: limit, + }, + }); + const items = getList(data, innerSharedContent, dateSort); + + return { + title: 'Popular posts on daily.dev', + link, + item: items, + 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/source.ts b/lib/routes/daily/source.ts index 4594daa6d462c1..34bfec4e688e01 100644 --- a/lib/routes/daily/source.ts +++ b/lib/routes/daily/source.ts @@ -1,4 +1,4 @@ -import { Route } from '@/types'; +import { DataItem, Route } from '@/types'; import { baseUrl, getBuildId, getData, getList } from './utils'; import ofetch from '@/utils/ofetch'; import cache from '@/utils/cache'; @@ -125,10 +125,18 @@ fragment UserPost on Post { }`; export const route: Route = { - path: '/source/:sourceId', + path: '/source/:sourceId/:innerSharedContent?', example: '/daily/source/hn', parameters: { sourceId: 'The source id', + innerSharedContent: { + description: 'Where to Fetch inner Shared Posts instead of original', + default: 'false', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, }, radar: [ { @@ -144,8 +152,9 @@ export const route: Route = { 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 innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; + const link = `${baseUrl}/sources/${sourceId}`; const buildId = await getBuildId(); const userData = (await cache.tryGet(`daily:source:${sourceId}`, async () => { @@ -167,7 +176,7 @@ async function handler(ctx) { loggedIn: false, }, }); - return getList(edges); + return getList(edges, innerSharedContent, true); }, config.cache.routeExpire, false @@ -177,7 +186,7 @@ async function handler(ctx) { title: `${userData.name} posts on daily.dev`, description: userData.description, link, - item: items, + item: items as DataItem[], image: userData.image, logo: userData.image, icon: userData.image, diff --git a/lib/routes/daily/squads.ts b/lib/routes/daily/squads.ts new file mode 100644 index 00000000000000..1aabbd2d9346c9 --- /dev/null +++ b/lib/routes/daily/squads.ts @@ -0,0 +1,266 @@ +import { Route, ViewType } from '@/types'; +import { baseUrl, getData, getList, variables } from './utils.js'; + +const sourceQuery = ` +query Source($handle: ID!) { + source(id: $handle) { + ...SquadBaseInfo + moderationPostCount + } + } + fragment SquadBaseInfo on Source { + ...SourceBaseInfo + referralUrl + createdAt + flags { + featured + totalPosts + totalViews + totalUpvotes + } + category { + id + title + slug + } + ...PrivilegedMembers + } + fragment SourceBaseInfo on Source { + id + active + handle + name + permalink + public + type + description + image + membersCount + currentMember { + ...CurrentMember + } + memberPostingRole + memberInviteRole + moderationRequired + } + fragment CurrentMember on SourceMember { + user { + id + } + permissions + role + referralToken + flags { + hideFeedPosts + collapsePinnedPosts + } + } + fragment PrivilegedMembers on Source { + privilegedMembers { + user { + id + name + image + permalink + username + bio + reputation + companies { + name + image + } + contentPreference { + status + } + } + role + } + } + +`; + +const query = ` + 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 + clickbaitTitleDetected + } + 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 + clickbaitTitleDetected + } + + + + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted + } +`; + +export const route: Route = { + path: '/squads/:squads/:innerSharedContent?', + example: '/daily/squads/watercooler', + view: ViewType.Articles, + parameters: { + innerSharedContent: { + description: 'Where to Fetch inner Shared Posts instead of original', + default: 'false', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + }, + radar: [ + { + source: ['app.daily.dev/squads/:squads'], + }, + ], + name: 'Squads', + maintainers: ['Rjnishant530'], + handler, + url: 'app.daily.dev/squads/discover', +}; + +async function handler(ctx) { + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; + const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; + const squads = ctx.req.param('squads'); + + const link = `${baseUrl}/squads/${squads}`; + + const { id, description, name } = await getData( + { + query: sourceQuery, + variables: { + handle: squads, + }, + }, + true + ); + + const data = await getData({ + query, + variables: { + ...variables, + source: id, + ranking: 'TIME', + supportedTypes: ['article', 'share', 'freeform', 'video:youtube', 'collection', 'welcome'], + first: limit, + }, + }); + const items = getList(data, innerSharedContent, true); + + return { + title: `${name} - daily.dev`, + link, + item: items, + description, + logo: `${baseUrl}/favicon-32x32.png`, + icon: `${baseUrl}/favicon-32x32.png`, + language: 'en-us', + }; +} diff --git a/lib/routes/daily/upvoted.ts b/lib/routes/daily/upvoted.ts index 8326d0618ed6d0..0633880f6e4f2e 100644 --- a/lib/routes/daily/upvoted.ts +++ b/lib/routes/daily/upvoted.ts @@ -1,84 +1,181 @@ -import { Route } from '@/types'; -import { baseUrl, getData, getList } from './utils.js'; - -const variables = { - period: 7, - first: 15, -}; +import { Route, ViewType } from '@/types'; +import { baseUrl, getData, getList, variables } from './utils.js'; const query = ` - query MostUpvotedFeed( + query MostUpvotedFeed( + $loggedIn: Boolean! = false $first: Int + $after: String $period: Int - $supportedTypes: [String!] = ["article","share","freeform"] + $supportedTypes: [String!] = ["article","share","freeform","video:youtube","collection"] + $source: ID + $tag: String ) { - page: mostUpvotedFeed(first: $first, period: $period, supportedTypes: $supportedTypes) { + page: mostUpvotedFeed(first: $first, after: $after, period: $period, supportedTypes: $supportedTypes, source: $source, tag: $tag) { ...FeedPostConnection } } - + fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } edges { node { ...FeedPost contentHtml + ...UserPost @include(if: $loggedIn) } } } - + fragment FeedPost on Post { - ...SharedPostInfo + ...FeedPostInfo + sharedPost { + id + title + image + readTime + permalink + commentsPermalink + createdAt + type + tags + source { + id + handle + permalink + image + } + slug + clickbaitTitleDetected + } + trending + feedMeta + collectionSources { + handle + image + } + numCollectionSources + updatedAt + slug } - - fragment SharedPostInfo on Post { + + fragment FeedPostInfo on Post { id title image readTime permalink commentsPermalink - summary createdAt + commented + bookmarked + views numUpvotes numComments + summary + bookmark { + remindAt + } author { - ...UserShortInfo + id + name + image + username + permalink } + type tags + source { + id + handle + name + permalink + image + type + } + userState { + vote + flags { + feedbackDismiss + } + } + slug + clickbaitTitleDetected } - fragment UserShortInfo on User { - id - name - image - permalink - username - bio + + + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted } `; export const route: Route = { - path: '/upvoted', - example: '/daily/upvoted', + path: '/upvoted/:period?/:innerSharedContent?/:dateSort?', + example: '/daily/upvoted/7', + view: ViewType.Articles, radar: [ { source: ['app.daily.dev/upvoted'], }, ], + parameters: { + innerSharedContent: { + description: 'Where to Fetch inner Shared Posts instead of original', + default: 'false', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + dateSort: { + description: 'Sort posts by publication date instead of popularity', + default: 'true', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + period: { + description: 'Period of Lookup', + default: '7', + options: [ + { value: '7', label: 'Last Week' }, + { value: '30', label: 'Last Month' }, + { value: '365', label: 'Last Year' }, + ], + }, + }, name: 'Most upvoted', maintainers: ['Rjnishant530'], handler, url: 'app.daily.dev/upvoted', }; -async function handler() { - const link = `${baseUrl}/upvoted`; +async function handler(ctx) { + const link = `${baseUrl}/posts/upvoted`; + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; + const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; + const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true; + const period = ctx.req.param('period') ? Number.parseInt(ctx.req.param('period'), 10) : 7; + const data = await getData({ query, - variables, + variables: { + ...variables, + period, + first: limit, + }, }); - const items = getList(data); + const items = getList(data, innerSharedContent, dateSort); return { title: 'Most upvoted posts for developers | daily.dev', diff --git a/lib/routes/daily/user.ts b/lib/routes/daily/user.ts index cdf7383684c7b7..579ea0bcbb8d1d 100644 --- a/lib/routes/daily/user.ts +++ b/lib/routes/daily/user.ts @@ -1,4 +1,4 @@ -import { Route } from '@/types'; +import { DataItem, Route } from '@/types'; import { baseUrl, getBuildId, getData, getList } from './utils'; import ofetch from '@/utils/ofetch'; import cache from '@/utils/cache'; @@ -151,13 +151,23 @@ const userPostQuery = ` }`; export const route: Route = { - path: '/user/:userId', + path: '/user/:userId/:innerSharedContent?', example: '/daily/user/kramer', radar: [ { source: ['app.daily.dev/:userId/posts', 'app.daily.dev/:userId'], }, ], + parameters: { + innerSharedContent: { + description: 'Where to Fetch inner Shared Posts instead of original', + default: 'false', + options: [ + { value: 'false', label: 'False' }, + { value: 'true', label: 'True' }, + ], + }, + }, name: 'User Posts', maintainers: ['TonyRL'], handler, @@ -167,7 +177,7 @@ export const route: Route = { async function handler(ctx) { const userId = ctx.req.param('userId'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 7; - + const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; const buildId = await getBuildId(); const userData = await cache.tryGet(`daily:user:${userId}`, async () => { @@ -191,7 +201,7 @@ async function handler(ctx) { loggedIn: false, }, }); - return getList(edges); + return getList(edges, innerSharedContent, true); }, config.cache.routeExpire, false @@ -201,7 +211,7 @@ async function handler(ctx) { title: `${user.name} | daily.dev`, description: user.bio, link: `${baseUrl}/${userId}/posts`, - item: items, + item: items as DataItem[], image: user.image, logo: user.image, icon: user.image, diff --git a/lib/routes/daily/utils.ts b/lib/routes/daily/utils.ts index 4576a869502544..eb559762dcb0a1 100644 --- a/lib/routes/daily/utils.ts +++ b/lib/routes/daily/utils.ts @@ -5,11 +5,15 @@ import { config } from '@/config'; import { art } from '@/utils/render'; import path from 'node:path'; import { getCurrentPath } from '@/utils/helpers'; +import { DataItem } from '@/types'; const __dirname = getCurrentPath(import.meta.url); export const baseUrl = 'https://app.daily.dev'; const gqlUrl = `https://api.daily.dev/graphql`; - +export const variables = { + version: 54, + loggedIn: false, +}; export const getBuildId = () => cache.tryGet( 'daily:buildId', @@ -22,30 +26,42 @@ export const getBuildId = () => false ); -export const getData = async (graphqlQuery) => { +export const getData = async (graphqlQuery, source = false) => { const response = await ofetch(gqlUrl, { method: 'POST', body: graphqlQuery, }); - return response.data.page.edges; + return source ? response.data.source : response.data.page.edges; }; const render = (data) => art(path.join(__dirname, 'templates/posts.art'), data); -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, - })); +export const getList = (edges, innerSharedContent: boolean, dateSort: boolean) => + edges.map(({ node }) => { + let link: string; + let title: string; + if (innerSharedContent && node.type === 'share') { + link = node.sharedPost.permalink; + title = node.sharedPost.title; + } else { + link = node.commentsPermalink ?? node.permalink; + title = node.title; + } + + return { + id: node.id, + title, + link, + 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: dateSort ? parseDate(node.createdAt) : '', + upvotes: node.numUpvotes, + comments: node.numComments, + category: node.tags, + } as DataItem; + }); diff --git a/lib/routes/stockedge/daily-news.ts b/lib/routes/stockedge/daily-news.ts index e7c88fad57ed36..5702c4617ef818 100644 --- a/lib/routes/stockedge/daily-news.ts +++ b/lib/routes/stockedge/daily-news.ts @@ -37,6 +37,9 @@ async function handler() { const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { + if (!item.securityID) { + return item; + } const info = await getData(`${apiInfo}/${item.securityID}`); item.description = item.description + '

' + info?.AboutCompanyText; return item; diff --git a/lib/routes/stockedge/utils.ts b/lib/routes/stockedge/utils.ts index 8e2958838f3ea2..33bd2b42ffea88 100644 --- a/lib/routes/stockedge/utils.ts +++ b/lib/routes/stockedge/utils.ts @@ -1,3 +1,4 @@ +import { DataItem } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; @@ -15,16 +16,20 @@ const getData = (url) => const getList = (data) => data.map((value) => { const { ID, Description: title, Date: createdOn, NewsitemSecurities, NewsitemSectors, NewsitemIndustries } = value; - const securityID = NewsitemSecurities[0].SecurityID; + const securityID = NewsitemSecurities?.[0]?.SecurityID; + const securitySlug = NewsitemSecurities?.[0]?.SecuritySlug; + const sectors = NewsitemSectors.map((v) => v.SectorName); + const industries = NewsitemIndustries.map((v) => v.IndustryName); return { id: ID, - title: `${title} [${NewsitemSectors.map((v) => v.SectorName).join(', ')}]`, + title: `${title} [${sectors.join(', ')}]`, description: title, securityID, - link: `${baseUrl}${NewsitemSecurities[0].SecuritySlug}/${securityID}`, + link: NewsitemSecurities?.length === 0 ? '' : `${baseUrl}${securitySlug}/${securityID}?section=news`, + guid: NewsitemSecurities?.length === 0 ? ID : `${baseUrl}${securitySlug}/${securityID}`, pubDate: parseDate(createdOn), - category: [...NewsitemIndustries.map((v) => v.IndustryName), ...NewsitemSectors.map((v) => v.SectorName)], - }; + category: [...industries, ...sectors], + } as DataItem; }); export { getData, getList };